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
parent
d2758b2f1d
commit
21f27c9a4d
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -254,6 +254,7 @@ public:
|
|||
static QByteArray convertToPostscriptFontFamilyName(const QByteArray &fontFamily);
|
||||
|
||||
virtual bool hasUnreliableGlyphOutline() const;
|
||||
virtual bool expectsGammaCorrectedBlending() const;
|
||||
|
||||
enum HintStyle {
|
||||
HintNone,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Reference in New Issue