Enable gamma-correction on a face-by-face basis

Changes how we control if gamma-correction is done, and enables it for
the freetype CFF engine when stem-darkening is available.

The new code replaces existing hacks to force gamma-correction off
when using Freetype on X11 and Windows.

Change-Id: Ic703ca6965a3d81b204349e10f406c991b292edd
Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>
bb10
Allan Sandfeld Jensen 2016-11-21 13:33:55 +01:00
parent d2758b2f1d
commit 21f27c9a4d
15 changed files with 72 additions and 40 deletions

View File

@ -24,6 +24,7 @@ SOURCES += \
$$PWD/src/base/ftbbox.c \
$$PWD/src/base/ftdebug.c \
$$PWD/src/base/ftglyph.c \
$$PWD/src/base/ftfntfmt.c \
$$PWD/src/base/ftinit.c \
$$PWD/src/base/ftlcdfil.c \
$$PWD/src/base/ftmm.c \

View File

@ -5547,7 +5547,7 @@ static void qt_alphamapblit_quint16(QRasterBuffer *rasterBuffer,
int x, int y, const QRgba64 &color,
const uchar *map,
int mapWidth, int mapHeight, int mapStride,
const QClipData *)
const QClipData *, bool /*useGammaCorrection*/)
{
const quint16 c = color.toRgb16();
quint16 *dest = reinterpret_cast<quint16*>(rasterBuffer->scanLine(y)) + x;
@ -5572,18 +5572,16 @@ static void qt_alphamapblit_quint16(QRasterBuffer *rasterBuffer,
}
}
static inline void rgbBlendPixel(quint32 *dst, int coverage, QRgba64 slinear, const QColorProfile *colorProfile)
static inline void rgbBlendPixel(quint32 *dst, int coverage, QRgba64 slinear, const QColorProfile *colorProfile, bool useGammaCorrection)
{
// Do a gammacorrected RGB alphablend...
const QRgba64 dlinear = colorProfile->toLinear64(*dst);
const QRgba64 dlinear = useGammaCorrection ? colorProfile->toLinear64(*dst) : QRgba64::fromArgb32(*dst);
QRgba64 blend = rgbBlend(dlinear, slinear, coverage);
*dst = colorProfile->fromLinear64(blend);
*dst = useGammaCorrection ? colorProfile->fromLinear64(blend) : toArgb32(blend);
}
Q_GUI_EXPORT bool qt_needs_a8_gamma_correction = false;
static inline void grayBlendPixel(quint32 *dst, int coverage, QRgba64 slinear, const QColorProfile *colorProfile)
{
// Do a gammacorrected gray alphablend...
@ -5598,7 +5596,7 @@ static void qt_alphamapblit_uint32(QRasterBuffer *rasterBuffer,
int x, int y, quint32 color,
const uchar *map,
int mapWidth, int mapHeight, int mapStride,
const QClipData *clip)
const QClipData *clip, bool useGammaCorrection)
{
const quint32 c = color;
const int destStride = rasterBuffer->bytesPerLine() / sizeof(quint32);
@ -5610,7 +5608,7 @@ static void qt_alphamapblit_uint32(QRasterBuffer *rasterBuffer,
const QRgba64 slinear = colorProfile->toLinear64(c);
bool opaque_src = (qAlpha(color) == 255);
bool doGrayBlendPixel = opaque_src && qt_needs_a8_gamma_correction;
bool doGrayBlendPixel = opaque_src && useGammaCorrection;
if (!clip) {
quint32 *dest = reinterpret_cast<quint32*>(rasterBuffer->scanLine(y)) + x;
@ -5680,9 +5678,9 @@ static void qt_alphamapblit_argb32(QRasterBuffer *rasterBuffer,
int x, int y, const QRgba64 &color,
const uchar *map,
int mapWidth, int mapHeight, int mapStride,
const QClipData *clip)
const QClipData *clip, bool useGammaCorrection)
{
qt_alphamapblit_uint32(rasterBuffer, x, y, color.toArgb32(), map, mapWidth, mapHeight, mapStride, clip);
qt_alphamapblit_uint32(rasterBuffer, x, y, color.toArgb32(), map, mapWidth, mapHeight, mapStride, clip, useGammaCorrection);
}
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
@ -5690,9 +5688,9 @@ static void qt_alphamapblit_rgba8888(QRasterBuffer *rasterBuffer,
int x, int y, const QRgba64 &color,
const uchar *map,
int mapWidth, int mapHeight, int mapStride,
const QClipData *clip)
const QClipData *clip, bool useGammaCorrection)
{
qt_alphamapblit_uint32(rasterBuffer, x, y, ARGB2RGBA(color.toArgb32()), map, mapWidth, mapHeight, mapStride, clip);
qt_alphamapblit_uint32(rasterBuffer, x, y, ARGB2RGBA(color.toArgb32()), map, mapWidth, mapHeight, mapStride, clip, useGammaCorrection);
}
#endif
@ -5704,7 +5702,7 @@ inline static int qRgbAvg(QRgb rgb)
static void qt_alphargbblit_argb32(QRasterBuffer *rasterBuffer,
int x, int y, const QRgba64 &color,
const uint *src, int mapWidth, int mapHeight, int srcStride,
const QClipData *clip)
const QClipData *clip, bool useGammaCorrection)
{
const quint32 c = color.toArgb32();
@ -5714,7 +5712,7 @@ static void qt_alphargbblit_argb32(QRasterBuffer *rasterBuffer,
if (!colorProfile)
return;
const QRgba64 slinear = colorProfile->toLinear64(c);
const QRgba64 slinear = useGammaCorrection ? colorProfile->toLinear64(c) : color;
if (sa == 0)
return;
@ -5729,7 +5727,7 @@ static void qt_alphargbblit_argb32(QRasterBuffer *rasterBuffer,
dst[i] = c;
} else if (coverage != 0xff000000) {
if (dst[i] >= 0xff000000) {
rgbBlendPixel(dst+i, coverage, slinear, colorProfile);
rgbBlendPixel(dst+i, coverage, slinear, colorProfile, useGammaCorrection);
} else {
// Give up and do a naive blend.
const int a = qRgbAvg(coverage);
@ -5765,7 +5763,7 @@ static void qt_alphargbblit_argb32(QRasterBuffer *rasterBuffer,
dst[xp] = c;
} else if (coverage != 0xff000000) {
if (dst[xp] >= 0xff000000) {
rgbBlendPixel(dst+xp, coverage, slinear, colorProfile);
rgbBlendPixel(dst+xp, coverage, slinear, colorProfile, useGammaCorrection);
} else {
// Give up and do a naive blend.
const int a = qRgbAvg(coverage);

View File

@ -539,7 +539,7 @@ void qt_alphamapblit_quint16_neon(QRasterBuffer *rasterBuffer,
int x, int y, const QRgba64 &color,
const uchar *bitmap,
int mapWidth, int mapHeight, int mapStride,
const QClipData *)
const QClipData *, bool /*useGammaCorrection*/)
{
quint16 *dest = reinterpret_cast<quint16*>(rasterBuffer->scanLine(y)) + x;
const int destStride = rasterBuffer->bytesPerLine() / sizeof(quint16);

View File

@ -91,7 +91,7 @@ void qt_alphamapblit_quint16_neon(QRasterBuffer *rasterBuffer,
int x, int y, const QRgba64 &color,
const uchar *bitmap,
int mapWidth, int mapHeight, int mapStride,
const QClipData *clip);
const QClipData *clip, bool /*useGammaCorrection*/);
void qt_scale_image_argb32_on_rgb16_neon(uchar *destPixels, int dbpl,
const uchar *srcPixels, int sbpl, int srch,

View File

@ -111,13 +111,13 @@ typedef void (*AlphamapBlitFunc)(QRasterBuffer *rasterBuffer,
int x, int y, const QRgba64 &color,
const uchar *bitmap,
int mapWidth, int mapHeight, int mapStride,
const QClipData *clip);
const QClipData *clip, bool useGammaCorrection);
typedef void (*AlphaRGBBlitFunc)(QRasterBuffer *rasterBuffer,
int x, int y, const QRgba64 &color,
const uint *rgbmask,
int mapWidth, int mapHeight, int mapStride,
const QClipData *clip);
const QClipData *clip, bool useGammaCorrection);
typedef void (*RectFillFunc)(QRasterBuffer *rasterBuffer,
int x, int y, int width, int height,

View File

@ -2555,7 +2555,7 @@ QRasterBuffer *QRasterPaintEngine::rasterBuffer()
/*!
\internal
*/
void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx,int ry,int w,int h)
void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx,int ry,int w,int h, bool useGammaCorrection)
{
Q_D(QRasterPaintEngine);
QRasterPaintEngineState *s = state();
@ -2610,14 +2610,14 @@ void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx
} else if (depth == 8) {
if (s->penData.alphamapBlit) {
s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
scanline, w, h, bpl, 0);
scanline, w, h, bpl, 0, useGammaCorrection);
return;
}
} else if (depth == 32) {
// (A)RGB Alpha mask where the alpha component is not used.
if (s->penData.alphaRGBBlit) {
s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
(const uint *) scanline, w, h, bpl / 4, 0);
(const uint *) scanline, w, h, bpl / 4, 0, useGammaCorrection);
return;
}
}
@ -2646,10 +2646,10 @@ void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx
}
if (depth == 8)
s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
scanline, w, h, bpl, clip);
scanline, w, h, bpl, clip, useGammaCorrection);
else if (depth == 32)
s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
(const uint *) scanline, w, h, bpl / 4, clip);
(const uint *) scanline, w, h, bpl / 4, clip, useGammaCorrection);
return;
}
}
@ -2807,7 +2807,8 @@ bool QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs,
alphaPenBlt(alphaMap->constBits(), alphaMap->bytesPerLine(), alphaMap->depth(),
qFloor(positions[i].x) + offset.x(),
qRound(positions[i].y) + offset.y(),
alphaMap->width(), alphaMap->height());
alphaMap->width(), alphaMap->height(),
fontEngine->expectsGammaCorrectedBlending());
fontEngine->unlockAlphaMapForGlyph();
}
@ -2868,7 +2869,7 @@ bool QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs,
drawImage(QPoint(x, y), QImage(glyphBits, c.w, c.h, bpl, image.format()));
s->matrix = originalTransform;
} else {
alphaPenBlt(glyphBits, bpl, depth, x, y, c.w, c.h);
alphaPenBlt(glyphBits, bpl, depth, x, y, c.w, c.h, fontEngine->expectsGammaCorrectedBlending());
}
}
}

View File

@ -225,7 +225,7 @@ public:
#endif
QRasterBuffer *rasterBuffer();
void alphaPenBlt(const void* src, int bpl, int depth, int rx,int ry,int w,int h);
void alphaPenBlt(const void* src, int bpl, int depth, int rx,int ry,int w,int h, bool useGammaCorrection);
Type type() const Q_DECL_OVERRIDE { return Raster; }

View File

@ -445,6 +445,11 @@ bool QFontEngine::supportsTransformation(const QTransform &transform) const
return transform.type() < QTransform::TxProject;
}
bool QFontEngine::expectsGammaCorrectedBlending() const
{
return true;
}
void QFontEngine::getGlyphPositions(const QGlyphLayout &glyphs, const QTransform &matrix, QTextItem::RenderFlags flags,
QVarLengthArray<glyph_t> &glyphs_out, QVarLengthArray<QFixedPoint> &positions)
{

View File

@ -61,17 +61,19 @@
#include FT_TRUETYPE_TABLES_H
#include FT_TYPE1_TABLES_H
#include FT_GLYPH_H
#include FT_MODULE_H
#if defined(FT_LCD_FILTER_H)
#include FT_LCD_FILTER_H
#define QT_USE_FREETYPE_LCDFILTER
#endif
#if defined(FT_CONFIG_OPTIONS_H)
#include FT_CONFIG_OPTIONS_H
#endif
#if defined(FT_LCD_FILTER_H)
#define QT_USE_FREETYPE_LCDFILTER
#if defined(FT_FONT_FORMATS_H)
#include FT_FONT_FORMATS_H
#endif
#ifdef QT_LINUXBASE
@ -151,6 +153,14 @@ QtFreetypeData *qt_getFreetypeData()
QtFreetypeData *&freetypeData = theFreetypeData()->localData();
if (!freetypeData)
freetypeData = new QtFreetypeData;
if (!freetypeData->library) {
FT_Init_FreeType(&freetypeData->library);
#if defined(FT_FONT_FORMATS_H)
// Freetype defaults to disabling stem-darkening on CFF, we re-enable it.
FT_Bool no_darkening = false;
FT_Property_Set(freetypeData->library, "cff", "no-stem-darkening", &no_darkening);
#endif
}
return freetypeData;
}
#endif
@ -158,8 +168,7 @@ QtFreetypeData *qt_getFreetypeData()
FT_Library qt_getFreetype()
{
QtFreetypeData *freetypeData = qt_getFreetypeData();
if (!freetypeData->library)
FT_Init_FreeType(&freetypeData->library);
Q_ASSERT(freetypeData->library);
return freetypeData->library;
}
@ -218,8 +227,6 @@ QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id,
return 0;
QtFreetypeData *freetypeData = qt_getFreetypeData();
if (!freetypeData->library)
FT_Init_FreeType(&freetypeData->library);
QFreetypeFace *freetype = freetypeData->faces.value(face_id, 0);
if (freetype) {
@ -687,6 +694,7 @@ QFontEngineFT::QFontEngineFT(const QFontDef &fd)
cacheEnabled = env.isEmpty() || env.toInt() == 0;
m_subPixelPositionCount = 4;
forceAutoHint = false;
stemDarkeningDriver = false;
}
QFontEngineFT::~QFontEngineFT()
@ -798,6 +806,17 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format,
}
}
}
#if defined(FT_FONT_FORMATS_H)
const char *fmt = FT_Get_Font_Format(face);
if (fmt && qstrncmp(fmt, "CFF", 4) == 0) {
FT_Bool no_stem_darkening = true;
FT_Error err = FT_Property_Get(qt_getFreetype(), "cff", "no-stem-darkening", &no_stem_darkening);
if (err == FT_Err_Ok)
stemDarkeningDriver = !no_stem_darkening;
else
stemDarkeningDriver = false;
}
#endif
fontDef.styleName = QString::fromUtf8(face->style_name);
@ -841,6 +860,11 @@ void QFontEngineFT::setDefaultHintStyle(HintStyle style)
default_hint_style = style;
}
bool QFontEngineFT::expectsGammaCorrectedBlending() const
{
return stemDarkeningDriver;
}
int QFontEngineFT::loadFlags(QGlyphSet *set, GlyphFormat format, int flags,
bool &hsubpixel, int &vfactor) const
{

View File

@ -252,6 +252,7 @@ private:
QPoint *offset) Q_DECL_OVERRIDE;
bool hasInternalCaching() const Q_DECL_OVERRIDE { return cacheEnabled; }
void unlockAlphaMapForGlyph() Q_DECL_OVERRIDE;
bool expectsGammaCorrectedBlending() const Q_DECL_OVERRIDE;
void removeGlyphFromCache(glyph_t glyph) Q_DECL_OVERRIDE;
int glyphMargin(QFontEngine::GlyphFormat /* format */) Q_DECL_OVERRIDE { return 0; }
@ -311,6 +312,7 @@ protected:
bool embeddedbitmap;
bool cacheEnabled;
bool forceAutoHint;
bool stemDarkeningDriver;
private:
friend class QFontEngineFTRawFont;

View File

@ -254,6 +254,7 @@ public:
static QByteArray convertToPostscriptFontFamilyName(const QByteArray &fontFamily);
virtual bool hasUnreliableGlyphOutline() const;
virtual bool expectsGammaCorrectedBlending() const;
enum HintStyle {
HintNone,

View File

@ -602,6 +602,11 @@ glyph_metrics_t QCoreTextFontEngine::alphaMapBoundingBox(glyph_t glyph, QFixed s
return br;
}
bool QCoreTextFontEngine::expectsGammaCorrectedBlending() const
{
// Only works well when font-smoothing is enabled
return (glyphFormat == Format_A32) && !(fontDef.styleStrategy & (QFont::NoAntialias | QFont::NoSubpixelAntialias));
}
QImage QCoreTextFontEngine::imageForGlyph(glyph_t glyph, QFixed subPixelPosition, bool aa, const QTransform &matrix)
{

View File

@ -110,6 +110,7 @@ public:
void doKerning(QGlyphLayout *g, ShaperFlags flags) const Q_DECL_OVERRIDE;
bool supportsTransformation(const QTransform &transform) const Q_DECL_OVERRIDE;
bool expectsGammaCorrectedBlending() const Q_DECL_OVERRIDE;
QFontEngine *cloneWithSize(qreal pixelSize) const Q_DECL_OVERRIDE;
Qt::HANDLE handle() const Q_DECL_OVERRIDE;

View File

@ -1207,8 +1207,6 @@ QWindowsFontEngineDataPtr sharedFontData()
}
#endif // QT_NO_THREAD
extern Q_GUI_EXPORT bool qt_needs_a8_gamma_correction;
QWindowsFontDatabase::QWindowsFontDatabase()
{
// Properties accessed by QWin32PrintEngine (Qt Print Support)
@ -1222,7 +1220,6 @@ QWindowsFontDatabase::QWindowsFontDatabase()
qCDebug(lcQpaFonts) << __FUNCTION__ << "Clear type: "
<< data->clearTypeEnabled << "gamma: " << data->fontSmoothingGamma;
}
qt_needs_a8_gamma_correction = true;
}
QWindowsFontDatabase::~QWindowsFontDatabase()

View File

@ -384,9 +384,6 @@ QVariant QXcbIntegration::styleHint(QPlatformIntegration::StyleHint hint) const
case QPlatformIntegration::PasswordMaskCharacter:
// TODO using various xcb, gnome or KDE settings
break; // Not implemented, use defaults
case QPlatformIntegration::FontSmoothingGamma:
// Match Qt 4.8 text rendering, and rendering of other X11 toolkits.
return qreal(1.0);
case QPlatformIntegration::StartDragDistance: {
// The default (in QPlatformTheme::defaultThemeHint) is 10 pixels, but
// on a high-resolution screen it makes sense to increase it.