2015-12-10 05:06:13 +02:00
|
|
|
/****************************************************************************
|
|
|
|
**
|
|
|
|
** Copyright (C) 2015 The Qt Company Ltd.
|
|
|
|
**
|
2019-06-03 13:38:02 +00:00
|
|
|
** This file is part of the QtGui module of the Katie Toolkit.
|
2015-12-10 05:06:13 +02:00
|
|
|
**
|
|
|
|
** $QT_BEGIN_LICENSE:LGPL$
|
|
|
|
** Commercial License Usage
|
|
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
|
|
** accordance with the commercial license agreement provided with the
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
|
|
|
** and conditions see http://www.qt.io/terms-conditions. For further
|
|
|
|
** information use the contact form at http://www.qt.io/contact-us.
|
|
|
|
**
|
|
|
|
** 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 or version 3 as published by the Free
|
|
|
|
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
|
|
|
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
|
|
|
** following information to ensure the GNU Lesser General Public License
|
|
|
|
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
|
|
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
|
|
**
|
|
|
|
** As a special exception, The Qt Company gives you certain additional
|
|
|
|
** rights. These rights are described in The Qt Company LGPL Exception
|
|
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
|
|
**
|
|
|
|
** 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.
|
|
|
|
**
|
|
|
|
** $QT_END_LICENSE$
|
|
|
|
**
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#include "qbitmap.h"
|
|
|
|
|
|
|
|
// #define FONTENGINE_DEBUG
|
|
|
|
|
|
|
|
#include <qapplication.h>
|
|
|
|
#include <qbytearray.h>
|
|
|
|
#include <qdebug.h>
|
|
|
|
#include <qtextcodec.h>
|
|
|
|
#include <qthread.h>
|
|
|
|
|
|
|
|
#include "qfontdatabase.h"
|
|
|
|
#include "qpaintdevice.h"
|
|
|
|
#include "qpainter.h"
|
|
|
|
#include "qvarlengtharray.h"
|
|
|
|
#include "qwidget.h"
|
|
|
|
#include "qsettings.h"
|
|
|
|
#include "qfile.h"
|
|
|
|
|
|
|
|
#include <qpaintengine_x11_p.h>
|
|
|
|
#include "qfont.h"
|
|
|
|
#include "qfont_p.h"
|
|
|
|
#include "qfontengine_p.h"
|
|
|
|
#include <qhash.h>
|
|
|
|
|
|
|
|
#include <qpainter_p.h>
|
|
|
|
#include <qunicodetables_p.h>
|
|
|
|
|
|
|
|
#include <qt_x11_p.h>
|
|
|
|
#include <qpixmap_x11_p.h>
|
|
|
|
#include "qx11info_x11.h"
|
|
|
|
#include "qfontengine_x11_p.h"
|
|
|
|
|
|
|
|
#include <limits.h>
|
|
|
|
|
|
|
|
#include <ft2build.h>
|
|
|
|
#if defined(FT_LCD_FILTER_H)
|
|
|
|
#include FT_LCD_FILTER_H
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(FC_LCD_FILTER)
|
|
|
|
|
|
|
|
#ifndef FC_LCD_FILTER_NONE
|
|
|
|
#define FC_LCD_FILTER_NONE FC_LCD_NONE
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef FC_LCD_FILTER_DEFAULT
|
|
|
|
#define FC_LCD_FILTER_DEFAULT FC_LCD_DEFAULT
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef FC_LCD_FILTER_LIGHT
|
|
|
|
#define FC_LCD_FILTER_LIGHT FC_LCD_LIGHT
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef FC_LCD_FILTER_LEGACY
|
|
|
|
#define FC_LCD_FILTER_LEGACY FC_LCD_LEGACY
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
|
|
|
|
#ifndef QT_NO_FONTCONFIG
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
// Multi FT engine
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
|
|
|
|
static QFontEngine *engineForPattern(FcPattern *match, const QFontDef &request, int screen)
|
|
|
|
{
|
|
|
|
QFontEngineX11FT *engine = new QFontEngineX11FT(match, request, screen);
|
|
|
|
if (!engine->invalid())
|
|
|
|
return engine;
|
|
|
|
|
|
|
|
delete engine;
|
|
|
|
QFontEngine *fe = new QFontEngineBox(request.pixelSize);
|
|
|
|
fe->fontDef = request;
|
|
|
|
return fe;
|
|
|
|
}
|
|
|
|
|
|
|
|
QFontEngineMultiFT::QFontEngineMultiFT(QFontEngine *fe, FcPattern *matchedPattern, FcPattern *p, int s, const QFontDef &req)
|
|
|
|
: QFontEngineMulti(2), request(req), pattern(p), fontSet(0), screen(s)
|
|
|
|
{
|
|
|
|
firstEnginePattern = FcPatternDuplicate(matchedPattern);
|
|
|
|
engines[0] = fe;
|
|
|
|
engines.at(0)->ref.ref();
|
|
|
|
fontDef = engines[0]->fontDef;
|
|
|
|
cache_cost = 100;
|
|
|
|
firstFontIndex = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
QFontEngineMultiFT::~QFontEngineMultiFT()
|
|
|
|
{
|
|
|
|
extern QMutex *qt_fontdatabase_mutex();
|
|
|
|
QMutexLocker locker(qt_fontdatabase_mutex());
|
|
|
|
|
|
|
|
FcPatternDestroy(pattern);
|
|
|
|
if (firstEnginePattern)
|
|
|
|
FcPatternDestroy(firstEnginePattern);
|
|
|
|
if (fontSet)
|
|
|
|
FcFontSetDestroy(fontSet);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void QFontEngineMultiFT::loadEngine(int at)
|
|
|
|
{
|
|
|
|
extern QMutex *qt_fontdatabase_mutex();
|
|
|
|
QMutexLocker locker(qt_fontdatabase_mutex());
|
|
|
|
|
|
|
|
extern QFontDef qt_FcPatternToQFontDef(FcPattern *pattern, const QFontDef &);
|
|
|
|
extern FcFontSet *qt_fontSetForPattern(FcPattern *pattern, const QFontDef &request);
|
|
|
|
|
|
|
|
Q_ASSERT(at > 0);
|
|
|
|
if (!fontSet) {
|
|
|
|
fontSet = qt_fontSetForPattern(pattern, request);
|
|
|
|
|
|
|
|
// it may happen that the fontset of fallbacks consists of only one font. In this case we
|
|
|
|
// have to fall back to the box fontengine as we cannot render the glyph.
|
|
|
|
if (fontSet->nfont == 1 && at == 1 && engines.size() == 2) {
|
|
|
|
Q_ASSERT(engines.at(at) == 0);
|
|
|
|
QFontEngine *fe = new QFontEngineBox(request.pixelSize);
|
|
|
|
fe->fontDef = request;
|
|
|
|
engines[at] = fe;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (firstEnginePattern) {
|
|
|
|
|
|
|
|
if (!FcPatternEqual(firstEnginePattern, fontSet->fonts[0]))
|
|
|
|
firstFontIndex = 0;
|
|
|
|
|
|
|
|
FcPatternDestroy(firstEnginePattern);
|
|
|
|
firstEnginePattern = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
engines.resize(fontSet->nfont + 1 - firstFontIndex);
|
|
|
|
}
|
|
|
|
Q_ASSERT(at < engines.size());
|
|
|
|
Q_ASSERT(engines.at(at) == 0);
|
|
|
|
|
|
|
|
FcPattern *match = FcFontRenderPrepare(NULL, pattern, fontSet->fonts[at + firstFontIndex - 1]);
|
|
|
|
QFontDef fontDef = qt_FcPatternToQFontDef(match, this->request);
|
|
|
|
|
|
|
|
// note: we use -1 for the script to make sure that we keep real
|
|
|
|
// FT engines separate from Multi engines in the font cache
|
|
|
|
QFontCache::Key key(fontDef, -1, screen);
|
|
|
|
QFontEngine *fontEngine = QFontCache::instance()->findEngine(key);
|
|
|
|
if (!fontEngine) {
|
|
|
|
fontEngine = engineForPattern(match, request, screen);
|
|
|
|
QFontCache::instance()->insertEngine(key, fontEngine);
|
|
|
|
}
|
|
|
|
FcPatternDestroy(match);
|
|
|
|
fontEngine->ref.ref();
|
|
|
|
engines[at] = fontEngine;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
// X11 FT engine
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Q_GUI_EXPORT void qt_x11ft_convert_pattern(FcPattern *pattern, QByteArray *file_name, int *index, bool *antialias)
|
|
|
|
{
|
|
|
|
FcChar8 *fileName;
|
|
|
|
FcPatternGetString(pattern, FC_FILE, 0, &fileName);
|
|
|
|
*file_name = (const char *)fileName;
|
|
|
|
if (!FcPatternGetInteger(pattern, FC_INDEX, 0, index))
|
|
|
|
index = 0;
|
|
|
|
FcBool b;
|
|
|
|
if (FcPatternGetBool(pattern, FC_ANTIALIAS, 0, &b) == FcResultMatch)
|
|
|
|
*antialias = b;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QFontEngineX11FT::QFontEngineX11FT(FcPattern *pattern, const QFontDef &fd, int screen)
|
|
|
|
: QFontEngineFT(fd)
|
|
|
|
{
|
|
|
|
// FcPatternPrint(pattern);
|
|
|
|
|
2017-08-09 11:51:23 +00:00
|
|
|
bool antialias = qt_x11Data->fc_antialias;
|
2015-12-10 05:06:13 +02:00
|
|
|
QByteArray file_name;
|
|
|
|
int face_index;
|
|
|
|
qt_x11ft_convert_pattern(pattern, &file_name, &face_index, &antialias);
|
|
|
|
QFontEngine::FaceId face_id;
|
|
|
|
face_id.filename = file_name;
|
|
|
|
face_id.index = face_index;
|
|
|
|
|
|
|
|
canUploadGlyphsToServer = QApplication::testAttribute(Qt::AA_X11InitThreads) || (qApp->thread() == QThread::currentThread());
|
|
|
|
|
|
|
|
subpixelType = Subpixel_None;
|
|
|
|
if (antialias) {
|
2017-08-09 11:51:23 +00:00
|
|
|
int subpixel = qt_x11Data->display ? qt_x11Data->screens[screen].subpixel : FC_RGBA_UNKNOWN;
|
2015-12-10 05:06:13 +02:00
|
|
|
if (subpixel == FC_RGBA_UNKNOWN)
|
|
|
|
(void) FcPatternGetInteger(pattern, FC_RGBA, 0, &subpixel);
|
|
|
|
if (!antialias || subpixel == FC_RGBA_UNKNOWN)
|
|
|
|
subpixel = FC_RGBA_NONE;
|
|
|
|
|
|
|
|
switch (subpixel) {
|
|
|
|
case FC_RGBA_NONE: subpixelType = Subpixel_None; break;
|
|
|
|
case FC_RGBA_RGB: subpixelType = Subpixel_RGB; break;
|
|
|
|
case FC_RGBA_BGR: subpixelType = Subpixel_BGR; break;
|
|
|
|
case FC_RGBA_VRGB: subpixelType = Subpixel_VRGB; break;
|
|
|
|
case FC_RGBA_VBGR: subpixelType = Subpixel_VBGR; break;
|
|
|
|
default: break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fd.hintingPreference != QFont::PreferDefaultHinting) {
|
|
|
|
switch (fd.hintingPreference) {
|
|
|
|
case QFont::PreferNoHinting:
|
|
|
|
default_hint_style = HintNone;
|
|
|
|
break;
|
|
|
|
case QFont::PreferVerticalHinting:
|
|
|
|
default_hint_style = HintLight;
|
|
|
|
break;
|
|
|
|
case QFont::PreferFullHinting:
|
|
|
|
default:
|
|
|
|
default_hint_style = HintFull;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#ifdef FC_HINT_STYLE
|
|
|
|
else {
|
|
|
|
int hint_style = 0;
|
2019-05-04 16:59:05 +00:00
|
|
|
if (FcPatternGetInteger (pattern, FC_HINT_STYLE, 0, &hint_style) == FcResultNoMatch
|
2017-08-09 11:51:23 +00:00
|
|
|
&& qt_x11Data->fc_hint_style > -1)
|
|
|
|
hint_style = qt_x11Data->fc_hint_style;
|
2015-12-10 05:06:13 +02:00
|
|
|
|
|
|
|
switch (hint_style) {
|
|
|
|
case FC_HINT_NONE:
|
|
|
|
default_hint_style = HintNone;
|
|
|
|
break;
|
|
|
|
case FC_HINT_SLIGHT:
|
|
|
|
default_hint_style = HintLight;
|
|
|
|
break;
|
|
|
|
case FC_HINT_MEDIUM:
|
|
|
|
default_hint_style = HintMedium;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
default_hint_style = HintFull;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(FC_AUTOHINT) && defined(FT_LOAD_FORCE_AUTOHINT)
|
|
|
|
{
|
|
|
|
bool autohint = false;
|
|
|
|
|
|
|
|
FcBool b;
|
|
|
|
if (FcPatternGetBool(pattern, FC_AUTOHINT, 0, &b) == FcResultMatch)
|
|
|
|
autohint = b;
|
|
|
|
|
|
|
|
if (autohint)
|
|
|
|
default_load_flags |= FT_LOAD_FORCE_AUTOHINT;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(FC_LCD_FILTER) && defined(FT_LCD_FILTER_H)
|
|
|
|
{
|
|
|
|
int filter = FC_LCD_FILTER_NONE;
|
|
|
|
if (FcPatternGetInteger(pattern, FC_LCD_FILTER, 0, &filter) == FcResultMatch) {
|
|
|
|
switch (filter) {
|
|
|
|
case FC_LCD_FILTER_NONE:
|
|
|
|
lcdFilterType = FT_LCD_FILTER_NONE;
|
|
|
|
break;
|
|
|
|
case FC_LCD_FILTER_DEFAULT:
|
|
|
|
lcdFilterType = FT_LCD_FILTER_DEFAULT;
|
|
|
|
break;
|
|
|
|
case FC_LCD_FILTER_LIGHT:
|
|
|
|
lcdFilterType = FT_LCD_FILTER_LIGHT;
|
|
|
|
break;
|
|
|
|
case FC_LCD_FILTER_LEGACY:
|
|
|
|
lcdFilterType = FT_LCD_FILTER_LEGACY;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// new unknown lcd filter type?!
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef FC_EMBEDDED_BITMAP
|
|
|
|
{
|
|
|
|
FcBool b;
|
|
|
|
if (FcPatternGetBool(pattern, FC_EMBEDDED_BITMAP, 0, &b) == FcResultMatch)
|
|
|
|
embeddedbitmap = b;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
GlyphFormat defaultFormat = Format_None;
|
|
|
|
|
|
|
|
#ifndef QT_NO_XRENDER
|
2017-08-09 11:51:23 +00:00
|
|
|
if (qt_x11Data->use_xrender) {
|
2015-12-10 05:06:13 +02:00
|
|
|
int format = PictStandardA8;
|
|
|
|
if (!antialias)
|
|
|
|
format = PictStandardA1;
|
|
|
|
else if (subpixelType == Subpixel_RGB
|
|
|
|
|| subpixelType == Subpixel_BGR
|
|
|
|
|| subpixelType == Subpixel_VRGB
|
|
|
|
|| subpixelType == Subpixel_VBGR)
|
|
|
|
format = PictStandardARGB32;
|
|
|
|
xglyph_format = format;
|
|
|
|
|
|
|
|
if (subpixelType != QFontEngineFT::Subpixel_None)
|
|
|
|
defaultFormat = Format_A32;
|
|
|
|
else if (antialias)
|
|
|
|
defaultFormat = Format_A8;
|
|
|
|
else
|
|
|
|
defaultFormat = Format_Mono;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (!init(face_id, antialias, defaultFormat))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!freetype->charset) {
|
|
|
|
FcCharSet *cs;
|
|
|
|
FcPatternGetCharSet (pattern, FC_CHARSET, 0, &cs);
|
|
|
|
freetype->charset = FcCharSetCopy(cs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QFontEngineX11FT::~QFontEngineX11FT()
|
|
|
|
{
|
|
|
|
freeGlyphSets();
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned long QFontEngineX11FT::allocateServerGlyphSet()
|
|
|
|
{
|
|
|
|
#ifndef QT_NO_XRENDER
|
2017-08-09 11:51:23 +00:00
|
|
|
if (!canUploadGlyphsToServer || !qt_x11Data->use_xrender)
|
2015-12-10 05:06:13 +02:00
|
|
|
return 0;
|
2017-08-09 11:51:23 +00:00
|
|
|
return XRenderCreateGlyphSet(qt_x11Data->display, XRenderFindStandardFormat(qt_x11Data->display, xglyph_format));
|
2015-12-10 05:06:13 +02:00
|
|
|
#else
|
|
|
|
return 0;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void QFontEngineX11FT::freeServerGlyphSet(unsigned long id)
|
|
|
|
{
|
|
|
|
#ifndef QT_NO_XRENDER
|
|
|
|
if (!id)
|
|
|
|
return;
|
2017-08-09 11:51:23 +00:00
|
|
|
XRenderFreeGlyphSet(qt_x11Data->display, id);
|
2015-12-10 05:06:13 +02:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QFontEngineX11FT::uploadGlyphToServer(QGlyphSet *set, uint glyphid, Glyph *g, GlyphInfo *info, int glyphDataSize) const
|
|
|
|
{
|
|
|
|
#ifndef QT_NO_XRENDER
|
|
|
|
if (!canUploadGlyphsToServer)
|
|
|
|
return false;
|
|
|
|
if (g->format == Format_Mono) {
|
|
|
|
/*
|
|
|
|
* swap bit order around; FreeType is always MSBFirst
|
|
|
|
*/
|
2017-08-09 11:51:23 +00:00
|
|
|
if (BitmapBitOrder(qt_x11Data->display) != MSBFirst) {
|
2015-12-10 05:06:13 +02:00
|
|
|
unsigned char *line = g->data;
|
|
|
|
int i = glyphDataSize;
|
|
|
|
while (i--) {
|
|
|
|
unsigned char c;
|
|
|
|
c = *line;
|
|
|
|
c = ((c << 1) & 0xaa) | ((c >> 1) & 0x55);
|
|
|
|
c = ((c << 2) & 0xcc) | ((c >> 2) & 0x33);
|
|
|
|
c = ((c << 4) & 0xf0) | ((c >> 4) & 0x0f);
|
|
|
|
*line++ = c;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
::Glyph xglyph = glyphid;
|
2017-08-09 11:51:23 +00:00
|
|
|
XRenderAddGlyphs (qt_x11Data->display, set->id, &xglyph, info, 1, (const char *)g->data, glyphDataSize);
|
2015-12-10 05:06:13 +02:00
|
|
|
delete [] g->data;
|
|
|
|
g->data = 0;
|
|
|
|
g->format = Format_None;
|
|
|
|
g->uploadedToServer = true;
|
|
|
|
return true;
|
|
|
|
#else
|
|
|
|
return false;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
QFontEngine *QFontEngineX11FT::cloneWithSize(qreal pixelSize) const
|
|
|
|
{
|
|
|
|
QFontDef fontDef;
|
|
|
|
fontDef.pixelSize = pixelSize;
|
|
|
|
QFontEngineX11FT *fe = new QFontEngineX11FT(fontDef);
|
|
|
|
if (!fe->initFromFontEngine(this)) {
|
|
|
|
delete fe;
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
#ifndef QT_NO_XRENDER
|
|
|
|
fe->xglyph_format = xglyph_format;
|
|
|
|
#endif
|
|
|
|
return fe;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // QT_NO_FONTCONFIG
|
|
|
|
|
|
|
|
QT_END_NAMESPACE
|
|
|
|
|
|
|
|
|
|
|
|
|