Simplify freetype rendering
Switch to always using FT_Render_Glyph for all glyph types. Change-Id: I9427bbffd30a8d1ca92d7ab9a92df8761f6b89c3 Reviewed-by: Lars Knoll <lars.knoll@qt.io>
This commit is contained in:
parent
b179abc33b
commit
e48b854087
@ -121,13 +121,12 @@ class QtFreetypeData
|
||||
{
|
||||
public:
|
||||
QtFreetypeData()
|
||||
: library(0), hasPatentFreeLcdRendering(false)
|
||||
: library(0)
|
||||
{ }
|
||||
~QtFreetypeData();
|
||||
|
||||
FT_Library library;
|
||||
QHash<QFontEngine::FaceId, QFreetypeFace *> faces;
|
||||
bool hasPatentFreeLcdRendering;
|
||||
};
|
||||
|
||||
QtFreetypeData::~QtFreetypeData()
|
||||
@ -153,11 +152,6 @@ QtFreetypeData *qt_getFreetypeData()
|
||||
FT_Bool no_darkening = false;
|
||||
FT_Property_Set(freetypeData->library, "cff", "no-stem-darkening", &no_darkening);
|
||||
#endif
|
||||
// FreeType has since 2.8.1 a patent free alternative to LCD-filtering.
|
||||
FT_Int amajor, aminor = 0, apatch = 0;
|
||||
FT_Library_Version(freetypeData->library, &amajor, &aminor, &apatch);
|
||||
if (QT_VERSION_CHECK(amajor, aminor, apatch) >= QT_VERSION_CHECK(2, 8, 1))
|
||||
freetypeData->hasPatentFreeLcdRendering = true;
|
||||
}
|
||||
return freetypeData;
|
||||
}
|
||||
@ -561,26 +555,7 @@ QFontEngineFT::Glyph::~Glyph()
|
||||
delete [] data;
|
||||
}
|
||||
|
||||
struct LcdFilterDummy
|
||||
{
|
||||
static inline void filterPixel(uchar &, uchar &, uchar &)
|
||||
{}
|
||||
};
|
||||
|
||||
struct LcdFilterLegacy
|
||||
{
|
||||
static inline void filterPixel(uchar &red, uchar &green, uchar &blue)
|
||||
{
|
||||
uint r = red, g = green, b = blue;
|
||||
// intra-pixel filter used by the legacy filter (adopted from _ft_lcd_filter_legacy)
|
||||
red = (r * uint(65538 * 9/13) + g * uint(65538 * 1/6) + b * uint(65538 * 1/13)) / 65536;
|
||||
green = (r * uint(65538 * 3/13) + g * uint(65538 * 4/6) + b * uint(65538 * 3/13)) / 65536;
|
||||
blue = (r * uint(65538 * 1/13) + g * uint(65538 * 1/6) + b * uint(65538 * 9/13)) / 65536;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename LcdFilter>
|
||||
static void convertRGBToARGB_helper(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr)
|
||||
static inline void convertRGBToARGB(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr)
|
||||
{
|
||||
const int offs = bgr ? -1 : 1;
|
||||
const int w = width * 3;
|
||||
@ -590,7 +565,6 @@ static void convertRGBToARGB_helper(const uchar *src, uint *dst, int width, int
|
||||
uchar red = src[x + 1 - offs];
|
||||
uchar green = src[x + 1];
|
||||
uchar blue = src[x + 1 + offs];
|
||||
LcdFilter::filterPixel(red, green, blue);
|
||||
*dd++ = (0xFFU << 24) | (red << 16) | (green << 8) | blue;
|
||||
}
|
||||
dst += width;
|
||||
@ -598,16 +572,7 @@ static void convertRGBToARGB_helper(const uchar *src, uint *dst, int width, int
|
||||
}
|
||||
}
|
||||
|
||||
static inline void convertRGBToARGB(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr, bool legacyFilter)
|
||||
{
|
||||
if (!legacyFilter)
|
||||
convertRGBToARGB_helper<LcdFilterDummy>(src, dst, width, height, src_pitch, bgr);
|
||||
else
|
||||
convertRGBToARGB_helper<LcdFilterLegacy>(src, dst, width, height, src_pitch, bgr);
|
||||
}
|
||||
|
||||
template <typename LcdFilter>
|
||||
static void convertRGBToARGB_V_helper(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr)
|
||||
static inline void convertRGBToARGB_V(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr)
|
||||
{
|
||||
const int offs = bgr ? -src_pitch : src_pitch;
|
||||
while (height--) {
|
||||
@ -615,54 +580,12 @@ static void convertRGBToARGB_V_helper(const uchar *src, uint *dst, int width, in
|
||||
uchar red = src[x + src_pitch - offs];
|
||||
uchar green = src[x + src_pitch];
|
||||
uchar blue = src[x + src_pitch + offs];
|
||||
LcdFilter::filterPixel(red, green, blue);
|
||||
*dst++ = (0XFFU << 24) | (red << 16) | (green << 8) | blue;
|
||||
}
|
||||
src += 3*src_pitch;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void convertRGBToARGB_V(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr, bool legacyFilter)
|
||||
{
|
||||
if (!legacyFilter)
|
||||
convertRGBToARGB_V_helper<LcdFilterDummy>(src, dst, width, height, src_pitch, bgr);
|
||||
else
|
||||
convertRGBToARGB_V_helper<LcdFilterLegacy>(src, dst, width, height, src_pitch, bgr);
|
||||
}
|
||||
|
||||
static inline void convertGRAYToARGB(const uchar *src, uint *dst, int width, int height, int src_pitch)
|
||||
{
|
||||
while (height--) {
|
||||
const uchar *p = src;
|
||||
const uchar * const e = p + width;
|
||||
while (p < e) {
|
||||
uchar gray = *p++;
|
||||
*dst++ = (0xFFU << 24) | (gray << 16) | (gray << 8) | gray;
|
||||
}
|
||||
src += src_pitch;
|
||||
}
|
||||
}
|
||||
|
||||
static void convoluteBitmap(const uchar *src, uchar *dst, int width, int height, int pitch)
|
||||
{
|
||||
// convolute the bitmap with a triangle filter to get rid of color fringes
|
||||
// If we take account for a gamma value of 2, we end up with
|
||||
// weights of 1, 4, 9, 4, 1. We use an approximation of 1, 3, 8, 3, 1 here,
|
||||
// as this nicely sums up to 16 :)
|
||||
int h = height;
|
||||
while (h--) {
|
||||
dst[0] = dst[1] = 0;
|
||||
//
|
||||
for (int x = 2; x < width - 2; ++x) {
|
||||
uint sum = src[x-2] + 3*src[x-1] + 8*src[x] + 3*src[x+1] + src[x+2];
|
||||
dst[x] = (uchar) (sum >> 4);
|
||||
}
|
||||
dst[width - 2] = dst[width - 1] = 0;
|
||||
src += pitch;
|
||||
dst += pitch;
|
||||
}
|
||||
}
|
||||
|
||||
static QFontEngine::SubpixelAntialiasingType subpixelAntialiasingTypeHint()
|
||||
{
|
||||
static int type = -1;
|
||||
@ -1153,196 +1076,97 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph,
|
||||
|
||||
int glyph_buffer_size = 0;
|
||||
QScopedArrayPointer<uchar> glyph_buffer;
|
||||
bool useFreetypeRenderGlyph = false;
|
||||
if (slot->format == FT_GLYPH_FORMAT_OUTLINE && (hsubpixel || vfactor != 1)) {
|
||||
err = FT_Library_SetLcdFilter(slot->library, (FT_LcdFilter)lcdFilterType);
|
||||
// We use FT_Render_Glyph if freetype has support for lcd-filtering
|
||||
// or is version 2.8.1 or higher and can do without.
|
||||
if (err == FT_Err_Ok || qt_getFreetypeData()->hasPatentFreeLcdRendering)
|
||||
useFreetypeRenderGlyph = true;
|
||||
FT_Render_Mode renderMode = (default_hint_style == HintLight) ? FT_RENDER_MODE_LIGHT : FT_RENDER_MODE_NORMAL;
|
||||
switch (format) {
|
||||
case Format_Mono:
|
||||
renderMode = FT_RENDER_MODE_MONO;
|
||||
break;
|
||||
case Format_A32:
|
||||
Q_ASSERT(hsubpixel || vfactor != 1);
|
||||
renderMode = hsubpixel ? FT_RENDER_MODE_LCD : FT_RENDER_MODE_LCD_V;
|
||||
break;
|
||||
case Format_A8:
|
||||
case Format_ARGB:
|
||||
break;
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
if (useFreetypeRenderGlyph) {
|
||||
err = FT_Render_Glyph(slot, hsubpixel ? FT_RENDER_MODE_LCD : FT_RENDER_MODE_LCD_V);
|
||||
FT_Library_SetLcdFilter(slot->library, (FT_LcdFilter)lcdFilterType);
|
||||
|
||||
if (err != FT_Err_Ok)
|
||||
qWarning("render glyph failed err=%x face=%p, glyph=%d", err, face, glyph);
|
||||
err = FT_Render_Glyph(slot, renderMode);
|
||||
if (err != FT_Err_Ok)
|
||||
qWarning("render glyph failed err=%x face=%p, glyph=%d", err, face, glyph);
|
||||
|
||||
FT_Library_SetLcdFilter(slot->library, FT_LCD_FILTER_NONE);
|
||||
FT_Library_SetLcdFilter(slot->library, FT_LCD_FILTER_NONE);
|
||||
|
||||
info.height = slot->bitmap.rows / vfactor;
|
||||
info.width = hsubpixel ? slot->bitmap.width / 3 : slot->bitmap.width;
|
||||
info.x = slot->bitmap_left;
|
||||
info.y = slot->bitmap_top;
|
||||
|
||||
glyph_buffer_size = info.width * info.height * 4;
|
||||
glyph_buffer.reset(new uchar[glyph_buffer_size]);
|
||||
|
||||
if (hsubpixel)
|
||||
convertRGBToARGB(slot->bitmap.buffer, (uint *)glyph_buffer.data(), info.width, info.height, slot->bitmap.pitch, subpixelType != Subpixel_RGB, false);
|
||||
else if (vfactor != 1)
|
||||
convertRGBToARGB_V(slot->bitmap.buffer, (uint *)glyph_buffer.data(), info.width, info.height, slot->bitmap.pitch, subpixelType != Subpixel_VRGB, false);
|
||||
} else {
|
||||
int left = slot->metrics.horiBearingX;
|
||||
int right = slot->metrics.horiBearingX + slot->metrics.width;
|
||||
int top = slot->metrics.horiBearingY;
|
||||
int bottom = slot->metrics.horiBearingY - slot->metrics.height;
|
||||
if (transform && slot->format != FT_GLYPH_FORMAT_BITMAP)
|
||||
transformBoundingBox(&left, &top, &right, &bottom, &matrix);
|
||||
left = FLOOR(left);
|
||||
right = CEIL(right);
|
||||
bottom = FLOOR(bottom);
|
||||
top = CEIL(top);
|
||||
|
||||
int hpixels = TRUNC(right - left);
|
||||
// subpixel position requires one more pixel
|
||||
if (subPixelPosition > 0 && format != Format_Mono)
|
||||
hpixels++;
|
||||
|
||||
if (hsubpixel)
|
||||
hpixels = hpixels*3 + 8;
|
||||
info.width = hpixels;
|
||||
info.height = TRUNC(top - bottom);
|
||||
info.x = TRUNC(left);
|
||||
info.y = TRUNC(top);
|
||||
if (hsubpixel) {
|
||||
info.width /= 3;
|
||||
info.x -= 1;
|
||||
}
|
||||
|
||||
// If any of the metrics are too large to fit, don't cache them
|
||||
if (areMetricsTooLarge(info))
|
||||
return 0;
|
||||
info.height = slot->bitmap.rows;
|
||||
info.width = slot->bitmap.width;
|
||||
info.x = slot->bitmap_left;
|
||||
info.y = slot->bitmap_top;
|
||||
if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_LCD)
|
||||
info.width = info.width / 3;
|
||||
if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_LCD_V)
|
||||
info.height = info.height / vfactor;
|
||||
|
||||
int pitch = (format == Format_Mono ? ((info.width + 31) & ~31) >> 3 :
|
||||
(format == Format_A8 ? (info.width + 3) & ~3 : info.width * 4));
|
||||
if (glyph_buffer_size < pitch * info.height) {
|
||||
glyph_buffer_size = pitch * info.height;
|
||||
glyph_buffer.reset(new uchar[glyph_buffer_size]);
|
||||
memset(glyph_buffer.data(), 0, glyph_buffer_size);
|
||||
}
|
||||
|
||||
if (slot->format == FT_GLYPH_FORMAT_OUTLINE) {
|
||||
FT_Bitmap bitmap;
|
||||
bitmap.rows = info.height*vfactor;
|
||||
bitmap.width = hpixels;
|
||||
bitmap.pitch = format == Format_Mono ? (((info.width + 31) & ~31) >> 3) : ((bitmap.width + 3) & ~3);
|
||||
int bitmap_buffer_size = bitmap.rows * bitmap.pitch;
|
||||
if (!hsubpixel && vfactor == 1 && format != Format_A32) {
|
||||
Q_ASSERT(glyph_buffer_size <= bitmap_buffer_size);
|
||||
bitmap.buffer = glyph_buffer.data();
|
||||
} else {
|
||||
bitmap.buffer = new uchar[bitmap_buffer_size];
|
||||
memset(bitmap.buffer, 0, bitmap_buffer_size);
|
||||
}
|
||||
bitmap.pixel_mode = format == Format_Mono ? FT_PIXEL_MODE_MONO : FT_PIXEL_MODE_GRAY;
|
||||
FT_Matrix matrix;
|
||||
matrix.xx = (hsubpixel ? 3 : 1) << 16;
|
||||
matrix.yy = vfactor << 16;
|
||||
matrix.yx = matrix.xy = 0;
|
||||
glyph_buffer_size = info.height * pitch;
|
||||
glyph_buffer.reset(new uchar[glyph_buffer_size]);
|
||||
|
||||
FT_Outline_Transform(&slot->outline, &matrix);
|
||||
FT_Outline_Translate (&slot->outline, (hsubpixel ? -3*left +(4<<6) : -left), -bottom*vfactor);
|
||||
FT_Outline_Get_Bitmap(slot->library, &slot->outline, &bitmap);
|
||||
if (hsubpixel) {
|
||||
Q_ASSERT (bitmap.pixel_mode == FT_PIXEL_MODE_GRAY);
|
||||
Q_ASSERT(antialias);
|
||||
uchar *convoluted = new uchar[bitmap_buffer_size];
|
||||
bool useLegacyLcdFilter = false;
|
||||
useLegacyLcdFilter = (lcdFilterType == FT_LCD_FILTER_LEGACY);
|
||||
uchar *buffer = bitmap.buffer;
|
||||
if (!useLegacyLcdFilter) {
|
||||
convoluteBitmap(bitmap.buffer, convoluted, bitmap.width, info.height, bitmap.pitch);
|
||||
buffer = convoluted;
|
||||
}
|
||||
convertRGBToARGB(buffer + 1, (uint *)glyph_buffer.data(), info.width, info.height, bitmap.pitch, subpixelType != Subpixel_RGB, useLegacyLcdFilter);
|
||||
delete [] convoluted;
|
||||
} else if (vfactor != 1) {
|
||||
convertRGBToARGB_V(bitmap.buffer, (uint *)glyph_buffer.data(), info.width, info.height, bitmap.pitch, subpixelType != Subpixel_VRGB, true);
|
||||
} else if (format == Format_A32 && bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) {
|
||||
convertGRAYToARGB(bitmap.buffer, (uint *)glyph_buffer.data(), info.width, info.height, bitmap.pitch);
|
||||
}
|
||||
|
||||
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
|
||||
if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
|
||||
Q_ASSERT(format == Format_Mono);
|
||||
uchar *src = slot->bitmap.buffer;
|
||||
uchar *dst = glyph_buffer.data();
|
||||
int h = slot->bitmap.rows;
|
||||
if (format == Format_Mono) {
|
||||
int bytes = ((info.width + 7) & ~7) >> 3;
|
||||
while (h--) {
|
||||
memcpy (dst, src, bytes);
|
||||
dst += pitch;
|
||||
src += slot->bitmap.pitch;
|
||||
}
|
||||
} else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
|
||||
if (hsubpixel) {
|
||||
while (h--) {
|
||||
uint *dd = (uint *)dst;
|
||||
*dd++ = 0;
|
||||
for (int x = 0; x < static_cast<int>(slot->bitmap.width); x++) {
|
||||
uint a = ((src[x >> 3] & (0x80 >> (x & 7))) ? 0xffffff : 0x000000);
|
||||
*dd++ = a;
|
||||
}
|
||||
*dd++ = 0;
|
||||
dst += pitch;
|
||||
src += slot->bitmap.pitch;
|
||||
}
|
||||
} else if (vfactor != 1) {
|
||||
while (h--) {
|
||||
uint *dd = (uint *)dst;
|
||||
for (int x = 0; x < static_cast<int>(slot->bitmap.width); x++) {
|
||||
uint a = ((src[x >> 3] & (0x80 >> (x & 7))) ? 0xffffff : 0x000000);
|
||||
*dd++ = a;
|
||||
}
|
||||
dst += pitch;
|
||||
src += slot->bitmap.pitch;
|
||||
}
|
||||
} else {
|
||||
while (h--) {
|
||||
for (int x = 0; x < static_cast<int>(slot->bitmap.width); x++) {
|
||||
unsigned char a = ((src[x >> 3] & (0x80 >> (x & 7))) ? 0xff : 0x00);
|
||||
dst[x] = a;
|
||||
}
|
||||
dst += pitch;
|
||||
src += slot->bitmap.pitch;
|
||||
}
|
||||
}
|
||||
|
||||
int bytes = ((info.width + 7) & ~7) >> 3;
|
||||
while (h--) {
|
||||
memcpy (dst, src, bytes);
|
||||
dst += pitch;
|
||||
src += slot->bitmap.pitch;
|
||||
}
|
||||
#if ((FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100) >= 20500)
|
||||
else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA)
|
||||
{
|
||||
while (h--) {
|
||||
} else if (slot->bitmap.pixel_mode == 7 /*FT_PIXEL_MODE_BGRA*/) {
|
||||
Q_ASSERT(format == Format_ARGB);
|
||||
uchar *src = slot->bitmap.buffer;
|
||||
uchar *dst = glyph_buffer.data();
|
||||
int h = slot->bitmap.rows;
|
||||
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;
|
||||
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);
|
||||
}
|
||||
info.width = info.linearAdvance = info.xOff = slot->bitmap.width;
|
||||
info.height = slot->bitmap.rows;
|
||||
info.x = slot->bitmap_left;
|
||||
info.y = slot->bitmap_top;
|
||||
}
|
||||
#else
|
||||
memcpy(dst, src, slot->bitmap.width * 4);
|
||||
#endif
|
||||
dst += slot->bitmap.pitch;
|
||||
src += slot->bitmap.pitch;
|
||||
}
|
||||
info.linearAdvance = info.xOff = slot->bitmap.width;
|
||||
} else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) {
|
||||
Q_ASSERT(format == Format_A8);
|
||||
uchar *src = slot->bitmap.buffer;
|
||||
uchar *dst = glyph_buffer.data();
|
||||
int h = slot->bitmap.rows;
|
||||
int bytes = info.width;
|
||||
while (h--) {
|
||||
memcpy (dst, src, bytes);
|
||||
dst += pitch;
|
||||
src += slot->bitmap.pitch;
|
||||
}
|
||||
} else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_LCD) {
|
||||
Q_ASSERT(format == Format_A32);
|
||||
convertRGBToARGB(slot->bitmap.buffer, (uint *)glyph_buffer.data(), info.width, info.height, slot->bitmap.pitch, subpixelType != Subpixel_RGB);
|
||||
} else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_LCD_V) {
|
||||
Q_ASSERT(format == Format_A32);
|
||||
convertRGBToARGB_V(slot->bitmap.buffer, (uint *)glyph_buffer.data(), info.width, info.height, slot->bitmap.pitch, subpixelType != Subpixel_VRGB);
|
||||
} else {
|
||||
qWarning("QFontEngine: Glyph neither outline nor bitmap format=%d", slot->format);
|
||||
qWarning("QFontEngine: Glyph rendered in unknown pixel_mode=%d", slot->bitmap.pixel_mode);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!g) {
|
||||
g = new Glyph;
|
||||
|
Loading…
x
Reference in New Issue
Block a user