1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtGui module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
43 #include "qmetatype.h"
44 #include "qtextstream.h"
46 #include "qfontengine_ft_p.h"
47 #include "private/qimage_p.h"
49 #ifndef QT_NO_FREETYPE
52 #include "qfileinfo.h"
53 #include "qthreadstorage.h"
56 #include "qfontengine_ft_p.h"
58 #include FT_FREETYPE_H
60 #include FT_SYNTHESIS_H
61 #include FT_TRUETYPE_TABLES_H
62 #include FT_TYPE1_TABLES_H
65 #if defined(FT_LCD_FILTER_H)
66 #include FT_LCD_FILTER_H
69 #if defined(FT_CONFIG_OPTIONS_H)
70 #include FT_CONFIG_OPTIONS_H
73 #if defined(FT_LCD_FILTER_H) && defined(FT_CONFIG_OPTION_SUBPIXEL_RENDERING)
74 #define QT_USE_FREETYPE_LCDFILTER
81 #if !defined(QT_MAX_CACHED_GLYPH_SIZE)
82 # define QT_MAX_CACHED_GLYPH_SIZE 64
88 * Freetype 2.1.7 and earlier used width/height
89 * for matching sizes in the BDF and PCF loaders.
90 * This has been fixed for 2.1.8.
92 #if (FREETYPE_MAJOR*10000+FREETYPE_MINOR*100+FREETYPE_PATCH) >= 20105
93 #define X_SIZE(face,i) ((face)->available_sizes[i].x_ppem)
94 #define Y_SIZE(face,i) ((face)->available_sizes[i].y_ppem)
96 #define X_SIZE(face,i) ((face)->available_sizes[i].width << 6)
97 #define Y_SIZE(face,i) ((face)->available_sizes[i].height << 6)
100 /* FreeType 2.1.10 starts to provide FT_GlyphSlot_Embolden */
101 #if (FREETYPE_MAJOR*10000+FREETYPE_MINOR*100+FREETYPE_PATCH) >= 20110
102 #define Q_FT_GLYPHSLOT_EMBOLDEN(slot) FT_GlyphSlot_Embolden(slot)
104 #define Q_FT_GLYPHSLOT_EMBOLDEN(slot)
107 /* FreeType 2.1.10 starts to provide FT_GlyphSlot_Oblique */
108 #if (FREETYPE_MAJOR*10000+FREETYPE_MINOR*100+FREETYPE_PATCH) >= 20110
109 #define Q_HAS_FT_GLYPHSLOT_OBLIQUE
110 #define Q_FT_GLYPHSLOT_OBLIQUE(slot) FT_GlyphSlot_Oblique(slot)
112 #define Q_FT_GLYPHSLOT_OBLIQUE(slot)
115 #define FLOOR(x) ((x) & -64)
116 #define CEIL(x) (((x)+63) & -64)
117 #define TRUNC(x) ((x) >> 6)
118 #define ROUND(x) (((x)+32) & -64)
120 static HB_Error hb_getSFntTable(void *font, HB_Tag tableTag, HB_Byte *buffer, HB_UInt *length)
122 #if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) > 20103
123 FT_Face face = (FT_Face)font;
124 FT_ULong ftlen = *length;
127 if ( !FT_IS_SFNT(face) )
128 return HB_Err_Invalid_Argument;
130 error = FT_Load_Sfnt_Table(face, tableTag, 0, buffer, &ftlen);
132 return (HB_Error)error;
134 return HB_Err_Invalid_Argument;
138 // -------------------------- Freetype support ------------------------------
148 QHash<QFontEngine::FaceId, QFreetypeFace *> faces;
152 Q_GLOBAL_STATIC(QtFreetypeData, theFreetypeData)
154 QtFreetypeData *qt_getFreetypeData()
156 return theFreetypeData();
159 Q_GLOBAL_STATIC(QThreadStorage<QtFreetypeData *>, theFreetypeData)
161 QtFreetypeData *qt_getFreetypeData()
163 QtFreetypeData *&freetypeData = theFreetypeData()->localData();
165 freetypeData = new QtFreetypeData;
170 FT_Library qt_getFreetype()
172 QtFreetypeData *freetypeData = qt_getFreetypeData();
173 if (!freetypeData->library)
174 FT_Init_FreeType(&freetypeData->library);
175 return freetypeData->library;
178 int QFreetypeFace::fsType() const
181 TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(face, ft_sfnt_os2);
183 fsType = os2->fsType;
187 HB_Error QFreetypeFace::getPointInOutline(HB_Glyph glyph, int flags, hb_uint32 point, HB_Fixed *xpos, HB_Fixed *ypos, hb_uint32 *nPoints)
189 if (HB_Error error = (HB_Error)FT_Load_Glyph(face, glyph, flags))
192 if (face->glyph->format != FT_GLYPH_FORMAT_OUTLINE)
193 return HB_Err_Invalid_SubTable;
195 *nPoints = face->glyph->outline.n_points;
199 if (point > *nPoints)
200 return HB_Err_Invalid_SubTable;
202 *xpos = face->glyph->outline.points[point].x;
203 *ypos = face->glyph->outline.points[point].y;
208 extern QByteArray qt_fontdata_from_index(int);
211 * One font file can contain more than one font (bold/italic for example)
212 * find the right one and return it.
214 * Returns the freetype face or 0 in case of an empty file or any other problems
215 * (like not being able to open the file)
217 QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id,
218 const QByteArray &fontData)
220 if (face_id.filename.isEmpty() && fontData.isEmpty())
223 QtFreetypeData *freetypeData = qt_getFreetypeData();
224 if (!freetypeData->library)
225 FT_Init_FreeType(&freetypeData->library);
227 QFreetypeFace *freetype = freetypeData->faces.value(face_id, 0);
231 QScopedPointer<QFreetypeFace> newFreetype(new QFreetypeFace);
233 if (!face_id.filename.isEmpty()) {
234 QString fileName = QString::fromUtf8(face_id.filename);
235 if (face_id.filename.startsWith(":qmemoryfonts/")) {
236 // from qfontdatabase.cpp
237 QByteArray idx = face_id.filename;
238 idx.remove(0, 14); // remove ':qmemoryfonts/'
240 newFreetype->fontData = qt_fontdata_from_index(idx.toInt(&ok));
242 newFreetype->fontData = QByteArray();
243 } else if (!QFileInfo(fileName).isNativePath()) {
244 QFile file(fileName);
245 if (!file.open(QIODevice::ReadOnly)) {
248 newFreetype->fontData = file.readAll();
251 newFreetype->fontData = fontData;
253 if (!newFreetype->fontData.isEmpty()) {
254 if (FT_New_Memory_Face(freetypeData->library, (const FT_Byte *)newFreetype->fontData.constData(), newFreetype->fontData.size(), face_id.index, &face)) {
257 } else if (FT_New_Face(freetypeData->library, face_id.filename, face_id.index, &face)) {
260 newFreetype->face = face;
262 newFreetype->hbFace = qHBNewFace(face, hb_getSFntTable);
263 Q_CHECK_PTR(newFreetype->hbFace);
264 newFreetype->ref.store(1);
265 newFreetype->xsize = 0;
266 newFreetype->ysize = 0;
267 newFreetype->matrix.xx = 0x10000;
268 newFreetype->matrix.yy = 0x10000;
269 newFreetype->matrix.xy = 0;
270 newFreetype->matrix.yx = 0;
271 newFreetype->unicode_map = 0;
272 newFreetype->symbol_map = 0;
273 #ifndef QT_NO_FONTCONFIG
274 newFreetype->charset = 0;
277 memset(newFreetype->cmapCache, 0, sizeof(newFreetype->cmapCache));
279 for (int i = 0; i < newFreetype->face->num_charmaps; ++i) {
280 FT_CharMap cm = newFreetype->face->charmaps[i];
281 switch(cm->encoding) {
282 case FT_ENCODING_UNICODE:
283 newFreetype->unicode_map = cm;
285 case FT_ENCODING_APPLE_ROMAN:
286 case FT_ENCODING_ADOBE_LATIN_1:
287 if (!newFreetype->unicode_map || newFreetype->unicode_map->encoding != FT_ENCODING_UNICODE)
288 newFreetype->unicode_map = cm;
290 case FT_ENCODING_ADOBE_CUSTOM:
291 case FT_ENCODING_MS_SYMBOL:
292 if (!newFreetype->symbol_map)
293 newFreetype->symbol_map = cm;
300 if (!FT_IS_SCALABLE(newFreetype->face) && newFreetype->face->num_fixed_sizes == 1)
301 FT_Set_Char_Size (face, X_SIZE(newFreetype->face, 0), Y_SIZE(newFreetype->face, 0), 0, 0);
304 FcPatternGetString(pattern, FC_FAMILY, 0, &name);
305 qDebug("%s: using maps: default: %x unicode: %x, symbol: %x", name,
306 newFreetype->face->charmap ? newFreetype->face->charmap->encoding : 0,
307 newFreetype->unicode_map ? newFreetype->unicode_map->encoding : 0,
308 newFreetype->symbol_map ? newFreetype->symbol_map->encoding : 0);
310 for (int i = 0; i < 256; i += 8)
311 qDebug(" %x: %d %d %d %d %d %d %d %d", i,
312 FcCharSetHasChar(newFreetype->charset, i), FcCharSetHasChar(newFreetype->charset, i),
313 FcCharSetHasChar(newFreetype->charset, i), FcCharSetHasChar(newFreetype->charset, i),
314 FcCharSetHasChar(newFreetype->charset, i), FcCharSetHasChar(newFreetype->charset, i),
315 FcCharSetHasChar(newFreetype->charset, i), FcCharSetHasChar(newFreetype->charset, i));
318 FT_Set_Charmap(newFreetype->face, newFreetype->unicode_map);
320 freetypeData->faces.insert(face_id, newFreetype.data());
322 newFreetype.take()->release(face_id);
323 // we could return null in principle instead of throwing
326 freetype = newFreetype.take();
331 void QFreetypeFace::release(const QFontEngine::FaceId &face_id)
333 QtFreetypeData *freetypeData = qt_getFreetypeData();
337 #ifndef QT_NO_FONTCONFIG
339 FcCharSetDestroy(charset);
341 if(freetypeData->faces.contains(face_id))
342 freetypeData->faces.take(face_id);
345 if (freetypeData->faces.isEmpty()) {
346 FT_Done_FreeType(freetypeData->library);
347 freetypeData->library = 0;
352 void QFreetypeFace::computeSize(const QFontDef &fontDef, int *xsize, int *ysize, bool *outline_drawing)
354 *ysize = qRound(fontDef.pixelSize * 64);
355 *xsize = *ysize * fontDef.stretch / 100;
356 *outline_drawing = false;
359 * Bitmap only faces must match exactly, so find the closest
360 * one (height dominant search)
362 if (!(face->face_flags & FT_FACE_FLAG_SCALABLE)) {
364 for (int i = 1; i < face->num_fixed_sizes; i++) {
365 if (qAbs(*ysize - Y_SIZE(face,i)) <
366 qAbs (*ysize - Y_SIZE(face, best)) ||
367 (qAbs (*ysize - Y_SIZE(face, i)) ==
368 qAbs (*ysize - Y_SIZE(face, best)) &&
369 qAbs (*xsize - X_SIZE(face, i)) <
370 qAbs (*xsize - X_SIZE(face, best)))) {
374 if (FT_Set_Char_Size (face, X_SIZE(face, best), Y_SIZE(face, best), 0, 0) == 0) {
375 *xsize = X_SIZE(face, best);
376 *ysize = Y_SIZE(face, best);
379 if (!(face->face_flags & FT_FACE_FLAG_SCALABLE) && ysize == 0 && face->num_fixed_sizes >= 1) {
380 // work around FT 2.1.10 problem with BDF without PIXEL_SIZE property
381 err = FT_Set_Pixel_Sizes(face, face->available_sizes[0].width, face->available_sizes[0].height);
382 if (err && face->num_fixed_sizes == 1)
383 err = 0; //even more of a workaround...
390 *outline_drawing = (*xsize > (QT_MAX_CACHED_GLYPH_SIZE<<6) || *ysize > (QT_MAX_CACHED_GLYPH_SIZE<<6));
394 QFontEngine::Properties QFreetypeFace::properties() const
396 QFontEngine::Properties p;
397 p.postscriptName = FT_Get_Postscript_Name(face);
398 PS_FontInfoRec font_info;
399 if (FT_Get_PS_Font_Info(face, &font_info) == 0)
400 p.copyright = font_info.notice;
401 if (FT_IS_SCALABLE(face)) {
402 p.ascent = face->ascender;
403 p.descent = -face->descender;
404 p.leading = face->height - face->ascender + face->descender;
405 p.emSquare = face->units_per_EM;
406 p.boundingBox = QRectF(face->bbox.xMin, -face->bbox.yMax,
407 face->bbox.xMax - face->bbox.xMin,
408 face->bbox.yMax - face->bbox.yMin);
410 p.ascent = QFixed::fromFixed(face->size->metrics.ascender);
411 p.descent = QFixed::fromFixed(-face->size->metrics.descender);
412 p.leading = QFixed::fromFixed(face->size->metrics.height - face->size->metrics.ascender + face->size->metrics.descender);
413 p.emSquare = face->size->metrics.y_ppem;
414 // p.boundingBox = QRectF(-p.ascent.toReal(), 0, (p.ascent + p.descent).toReal(), face->size->metrics.max_advance/64.);
415 p.boundingBox = QRectF(0, -p.ascent.toReal(),
416 face->size->metrics.max_advance/64, (p.ascent + p.descent).toReal() );
419 p.capHeight = p.ascent;
420 p.lineWidth = face->underline_thickness;
424 bool QFreetypeFace::getSfntTable(uint tag, uchar *buffer, uint *length) const
427 #if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) > 20103
428 if (FT_IS_SFNT(face)) {
429 FT_ULong len = *length;
430 result = FT_Load_Sfnt_Table(face, tag, 0, buffer, &len) == FT_Err_Ok;
437 /* Some fonts (such as MingLiu rely on hinting to scale different
438 components to their correct sizes. While this is really broken (it
439 should be done in the component glyph itself, not the hinter) we
440 will have to live with it.
442 This means we can not use FT_LOAD_NO_HINTING to get the glyph
443 outline. All we can do is to load the unscaled glyph and scale it
444 down manually when required.
446 static void scaleOutline(FT_Face face, FT_GlyphSlot g, FT_Fixed x_scale, FT_Fixed y_scale)
448 x_scale = FT_MulDiv(x_scale, 1 << 10, face->units_per_EM);
449 y_scale = FT_MulDiv(y_scale, 1 << 10, face->units_per_EM);
450 FT_Vector *p = g->outline.points;
451 const FT_Vector *e = p + g->outline.n_points;
453 p->x = FT_MulFix(p->x, x_scale);
454 p->y = FT_MulFix(p->y, y_scale);
459 void QFreetypeFace::addGlyphToPath(FT_Face face, FT_GlyphSlot g, const QFixedPoint &point, QPainterPath *path, FT_Fixed x_scale, FT_Fixed y_scale)
461 const qreal factor = 1/64.;
462 scaleOutline(face, g, x_scale, y_scale);
464 QPointF cp = point.toPointF();
466 // convert the outline to a painter path
468 for (int j = 0; j < g->outline.n_contours; ++j) {
469 int last_point = g->outline.contours[j];
470 QPointF start = cp + QPointF(g->outline.points[i].x*factor, -g->outline.points[i].y*factor);
471 if(!(g->outline.tags[i] & 1)) {
472 start += cp + QPointF(g->outline.points[last_point].x*factor, -g->outline.points[last_point].y*factor);
475 // qDebug("contour: %d -- %d", i, g->outline.contours[j]);
476 // qDebug("first point at %f %f", start.x(), start.y());
482 while (i < last_point) {
484 c[n] = cp + QPointF(g->outline.points[i].x*factor, -g->outline.points[i].y*factor);
485 // qDebug() << " i=" << i << " flag=" << (int)g->outline.tags[i] << "point=" << c[n];
487 switch (g->outline.tags[i] & 3) {
489 // cubic bezier element
492 c[3] = (c[3] + c[2])/2;
496 // quadratic bezier element
499 c[3] = (c[1] + c[2])/2;
500 c[2] = (2*c[1] + c[3])/3;
501 c[1] = (2*c[1] + c[0])/3;
507 // qDebug() << "lineTo" << c[1];
514 c[2] = (2*c[1] + c[3])/3;
515 c[1] = (2*c[1] + c[0])/3;
519 // qDebug() << "cubicTo" << c[1] << c[2] << c[3];
520 path->cubicTo(c[1], c[2], c[3]);
525 // qDebug() << "closeSubpath";
526 path->closeSubpath();
530 c[2] = (2*c[1] + c[3])/3;
531 c[1] = (2*c[1] + c[0])/3;
533 // qDebug() << "cubicTo" << c[1] << c[2] << c[3];
534 path->cubicTo(c[1], c[2], c[3]);
540 extern void qt_addBitmapToPath(qreal x0, qreal y0, const uchar *image_data, int bpl, int w, int h, QPainterPath *path);
542 void QFreetypeFace::addBitmapToPath(FT_GlyphSlot slot, const QFixedPoint &point, QPainterPath *path, bool)
544 if (slot->format != FT_GLYPH_FORMAT_BITMAP
545 || slot->bitmap.pixel_mode != FT_PIXEL_MODE_MONO)
548 QPointF cp = point.toPointF();
549 qt_addBitmapToPath(cp.x() + TRUNC(slot->metrics.horiBearingX), cp.y() - TRUNC(slot->metrics.horiBearingY),
550 slot->bitmap.buffer, slot->bitmap.pitch, slot->bitmap.width, slot->bitmap.rows, path);
553 QFontEngineFT::Glyph::~Glyph()
558 static const uint subpixel_filter[3][3] = {
564 static inline uint filterPixel(uint red, uint green, uint blue, bool legacyFilter)
568 uint high = (red*subpixel_filter[0][0] + green*subpixel_filter[0][1] + blue*subpixel_filter[0][2]) >> 8;
569 uint mid = (red*subpixel_filter[1][0] + green*subpixel_filter[1][1] + blue*subpixel_filter[1][2]) >> 8;
570 uint low = (red*subpixel_filter[2][0] + green*subpixel_filter[2][1] + blue*subpixel_filter[2][2]) >> 8;
571 res = (mid << 24) + (high << 16) + (mid << 8) + low;
574 res = (alpha << 24) + (red << 16) + (green << 8) + blue;
579 static void convertRGBToARGB(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr, bool legacyFilter)
582 const int offs = bgr ? -1 : 1;
583 const int w = width * 3;
586 for (int x = 0; x < w; x += 3) {
587 uint red = src[x+1-offs];
588 uint green = src[x+1];
589 uint blue = src[x+1+offs];
590 *dd = filterPixel(red, green, blue, legacyFilter);
598 static void convertRGBToARGB_V(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr, bool legacyFilter)
601 const int offs = bgr ? -src_pitch : src_pitch;
603 for (int x = 0; x < width; x++) {
604 uint red = src[x+src_pitch-offs];
605 uint green = src[x+src_pitch];
606 uint blue = src[x+src_pitch+offs];
607 dst[x] = filterPixel(red, green, blue, legacyFilter);
614 static void convoluteBitmap(const uchar *src, uchar *dst, int width, int height, int pitch)
616 // convolute the bitmap with a triangle filter to get rid of color fringes
617 // If we take account for a gamma value of 2, we end up with
618 // weights of 1, 4, 9, 4, 1. We use an approximation of 1, 3, 8, 3, 1 here,
619 // as this nicely sums up to 16 :)
624 for (int x = 2; x < width - 2; ++x) {
625 uint sum = src[x-2] + 3*src[x-1] + 8*src[x] + 3*src[x+1] + src[x+2];
626 dst[x] = (uchar) (sum >> 4);
628 dst[width - 2] = dst[width - 1] = 0;
634 QFontEngineFT::QFontEngineFT(const QFontDef &fd)
642 kerning_pairs_loaded = false;
648 default_load_flags = FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
650 default_hint_style = HintNone;
652 default_hint_style = HintFull;
654 subpixelType = Subpixel_None;
656 #if defined(FT_LCD_FILTER_H)
657 lcdFilterType = (int)((quintptr) FT_LCD_FILTER_DEFAULT);
659 defaultFormat = Format_None;
660 embeddedbitmap = false;
661 cacheEnabled = qgetenv("QT_NO_FT_CACHE").isEmpty() || qgetenv("QT_NO_FT_CACHE").toInt() == 0;
662 m_subPixelPositionCount = 4;
665 QFontEngineFT::~QFontEngineFT()
668 freetype->release(face_id);
669 hbFace = 0; // we share the face in QFreeTypeFace, don't let ~QFontEngine delete it
672 bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format,
673 const QByteArray &fontData)
675 return init(faceId, antialias, format, QFreetypeFace::getFace(faceId, fontData));
678 bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format,
679 QFreetypeFace *freetypeFace)
681 freetype = freetypeFace;
687 defaultFormat = format;
688 this->antialias = antialias;
691 glyphFormat = QFontEngineGlyphCache::Raster_Mono;
692 else if (format == Format_A8)
693 glyphFormat = QFontEngineGlyphCache::Raster_A8;
694 else if (format == Format_A32)
695 glyphFormat = QFontEngineGlyphCache::Raster_RGBMask;
699 symbol = freetype->symbol_map != 0;
700 PS_FontInfoRec psrec;
701 // don't assume that type1 fonts are symbol fonts by default
702 if (FT_Get_PS_Font_Info(freetype->face, &psrec) == FT_Err_Ok) {
703 symbol = bool(fontDef.family.contains(QLatin1String("symbol"), Qt::CaseInsensitive));
706 freetype->hbFace->isSymbolFont = symbol;
708 lbearing = rbearing = SHRT_MIN;
709 freetype->computeSize(fontDef, &xsize, &ysize, &defaultGlyphSet.outline_drawing);
711 FT_Face face = lockFace();
713 if (FT_IS_SCALABLE(face)) {
714 bool fake_oblique = (fontDef.style != QFont::StyleNormal) && !(face->style_flags & FT_STYLE_FLAG_ITALIC);
716 #if !defined(Q_HAS_FT_GLYPHSLOT_OBLIQUE)
717 matrix.xy = 0x10000*3/10;
723 FT_Set_Transform(face, &matrix, 0);
724 freetype->matrix = matrix;
726 if ((fontDef.weight == QFont::Bold) && !(face->style_flags & FT_STYLE_FLAG_BOLD) && !FT_IS_FIXED_WIDTH(face))
729 line_thickness = QFixed::fromFixed(FT_MulFix(face->underline_thickness, face->size->metrics.y_scale));
730 underline_position = QFixed::fromFixed(-FT_MulFix(face->underline_position, face->size->metrics.y_scale));
732 // copied from QFontEngineQPF
734 int score = fontDef.weight * fontDef.pixelSize;
735 line_thickness = score / 700;
736 // looks better with thicker line for small pointsizes
737 if (line_thickness < 2 && score >= 1050)
739 underline_position = ((line_thickness * 2) + 3) / 6;
741 if (line_thickness < 1)
744 hbFont.x_ppem = face->size->metrics.x_ppem;
745 hbFont.y_ppem = face->size->metrics.y_ppem;
746 hbFont.x_scale = face->size->metrics.x_scale;
747 hbFont.y_scale = face->size->metrics.y_scale;
749 hbFace = freetype->hbFace;
751 metrics = face->size->metrics;
754 TrueType fonts with embedded bitmaps may have a bitmap font specific
755 ascent/descent in the EBLC table. There is no direct public API
756 to extract those values. The only way we've found is to trick freetype
757 into thinking that it's not a scalable font in FT_SelectSize so that
758 the metrics are retrieved from the bitmap strikes.
760 if (FT_IS_SCALABLE(face)) {
761 for (int i = 0; i < face->num_fixed_sizes; ++i) {
762 if (xsize == X_SIZE(face, i) && ysize == Y_SIZE(face, i)) {
763 face->face_flags &= ~FT_FACE_FLAG_SCALABLE;
765 FT_Select_Size(face, i);
766 metrics.ascender = face->size->metrics.ascender;
767 metrics.descender = face->size->metrics.descender;
768 FT_Set_Char_Size(face, xsize, ysize, 0, 0);
770 face->face_flags |= FT_FACE_FLAG_SCALABLE;
776 fontDef.styleName = QString::fromUtf8(face->style_name);
780 fsType = freetype->fsType();
784 void QFontEngineFT::setDefaultHintStyle(HintStyle style)
786 default_hint_style = style;
789 int QFontEngineFT::loadFlags(QGlyphSet *set, GlyphFormat format, int flags,
790 bool &hsubpixel, int &vfactor) const
792 int load_flags = FT_LOAD_DEFAULT | default_load_flags;
793 int load_target = default_hint_style == HintLight
794 ? FT_LOAD_TARGET_LIGHT
795 : FT_LOAD_TARGET_NORMAL;
797 if (format == Format_Mono) {
798 load_target = FT_LOAD_TARGET_MONO;
799 } else if (format == Format_A32) {
800 if (subpixelType == QFontEngineFT::Subpixel_RGB || subpixelType == QFontEngineFT::Subpixel_BGR) {
801 if (default_hint_style == HintFull)
802 load_target = FT_LOAD_TARGET_LCD;
804 } else if (subpixelType == QFontEngineFT::Subpixel_VRGB || subpixelType == QFontEngineFT::Subpixel_VBGR) {
805 if (default_hint_style == HintFull)
806 load_target = FT_LOAD_TARGET_LCD_V;
811 if (set && set->outline_drawing)
812 load_flags = FT_LOAD_NO_BITMAP;
814 if (default_hint_style == HintNone || (flags & HB_ShaperFlag_UseDesignMetrics) || (set && set->outline_drawing))
815 load_flags |= FT_LOAD_NO_HINTING;
817 load_flags |= load_target;
822 QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph,
823 QFixed subPixelPosition,
825 bool fetchMetricsOnly) const
827 // Q_ASSERT(freetype->lock == 1);
829 if (format == Format_None) {
830 if (defaultFormat != Format_None) {
831 format = defaultFormat;
833 format = Format_Mono;
837 Glyph *g = set ? set->getGlyph(glyph, subPixelPosition) : 0;
838 if (g && g->format == format)
841 QFontEngineFT::GlyphInfo info;
843 Q_ASSERT(format != Format_None);
844 bool hsubpixel = false;
846 int load_flags = loadFlags(set, format, 0, hsubpixel, vfactor);
848 if (format != Format_Mono && !embeddedbitmap)
849 load_flags |= FT_LOAD_NO_BITMAP;
851 FT_Matrix matrix = freetype->matrix;
852 bool transform = matrix.xx != 0x10000
853 || matrix.yy != 0x10000
858 load_flags |= FT_LOAD_NO_BITMAP;
860 FT_Face face = freetype->face;
863 v.x = format == Format_Mono ? 0 : FT_Pos(subPixelPosition.toReal() * 64);
865 FT_Set_Transform(face, &freetype->matrix, &v);
867 FT_Error err = FT_Load_Glyph(face, glyph, load_flags);
868 if (err && (load_flags & FT_LOAD_NO_BITMAP)) {
869 load_flags &= ~FT_LOAD_NO_BITMAP;
870 err = FT_Load_Glyph(face, glyph, load_flags);
872 if (err == FT_Err_Too_Few_Arguments) {
873 // this is an error in the bytecode interpreter, just try to run without it
874 load_flags |= FT_LOAD_FORCE_AUTOHINT;
875 err = FT_Load_Glyph(face, glyph, load_flags);
877 if (err != FT_Err_Ok)
878 qWarning("load glyph failed err=%x face=%p, glyph=%d", err, face, glyph);
880 if ((!set || set->outline_drawing) && fetchMetricsOnly)
883 FT_GlyphSlot slot = face->glyph;
884 if (embolden) Q_FT_GLYPHSLOT_EMBOLDEN(slot);
886 Q_FT_GLYPHSLOT_OBLIQUE(slot);
888 // While Embolden alters the metrics of the slot, oblique does not, so we need
889 // to fix this ourselves.
897 FT_Matrix_Multiply(&m, &matrix);
900 FT_Library library = qt_getFreetype();
902 info.xOff = TRUNC(ROUND(slot->advance.x));
905 uchar *glyph_buffer = 0;
906 int glyph_buffer_size = 0;
907 #if defined(QT_USE_FREETYPE_LCDFILTER)
908 bool useFreetypeRenderGlyph = false;
909 if (slot->format == FT_GLYPH_FORMAT_OUTLINE && (hsubpixel || vfactor != 1)) {
910 err = FT_Library_SetLcdFilter(library, (FT_LcdFilter)lcdFilterType);
911 if (err == FT_Err_Ok)
912 useFreetypeRenderGlyph = true;
915 if (useFreetypeRenderGlyph) {
916 err = FT_Render_Glyph(slot, hsubpixel ? FT_RENDER_MODE_LCD : FT_RENDER_MODE_LCD_V);
918 if (err != FT_Err_Ok)
919 qWarning("render glyph failed err=%x face=%p, glyph=%d", err, face, glyph);
921 FT_Library_SetLcdFilter(library, FT_LCD_FILTER_NONE);
923 info.height = slot->bitmap.rows / vfactor;
924 info.width = hsubpixel ? slot->bitmap.width / 3 : slot->bitmap.width;
925 info.x = -slot->bitmap_left;
926 info.y = slot->bitmap_top;
928 glyph_buffer_size = info.width * info.height * 4;
929 glyph_buffer = new uchar[glyph_buffer_size];
932 convertRGBToARGB(slot->bitmap.buffer, (uint *)glyph_buffer, info.width, info.height, slot->bitmap.pitch, subpixelType != QFontEngineFT::Subpixel_RGB, false);
933 else if (vfactor != 1)
934 convertRGBToARGB_V(slot->bitmap.buffer, (uint *)glyph_buffer, info.width, info.height, slot->bitmap.pitch, subpixelType != QFontEngineFT::Subpixel_VRGB, false);
938 int left = slot->metrics.horiBearingX;
939 int right = slot->metrics.horiBearingX + slot->metrics.width;
940 int top = slot->metrics.horiBearingY;
941 int bottom = slot->metrics.horiBearingY - slot->metrics.height;
942 if(transform && slot->format != FT_GLYPH_FORMAT_BITMAP) {
947 FT_Vector_Transform(&vector, &matrix);
952 FT_Vector_Transform(&vector, &matrix);
953 if (l > vector.x) l = vector.x;
954 if (r < vector.x) r = vector.x;
955 if (t < vector.y) t = vector.y;
956 if (b > vector.y) b = vector.y;
959 FT_Vector_Transform(&vector, &matrix);
960 if (l > vector.x) l = vector.x;
961 if (r < vector.x) r = vector.x;
962 if (t < vector.y) t = vector.y;
963 if (b > vector.y) b = vector.y;
966 FT_Vector_Transform(&vector, &matrix);
967 if (l > vector.x) l = vector.x;
968 if (r < vector.x) r = vector.x;
969 if (t < vector.y) t = vector.y;
970 if (b > vector.y) b = vector.y;
978 bottom = FLOOR(bottom);
981 int hpixels = TRUNC(right - left);
982 // subpixel position requires one more pixel
983 if (subPixelPosition > 0 && format != Format_Mono)
987 hpixels = hpixels*3 + 8;
988 info.width = hpixels;
989 info.height = TRUNC(top - bottom);
990 info.x = -TRUNC(left);
997 bool large_glyph = (((short)(slot->linearHoriAdvance>>10) != slot->linearHoriAdvance>>10)
998 || ((uchar)(info.width) != info.width)
999 || ((uchar)(info.height) != info.height)
1000 || ((signed char)(info.x) != info.x)
1001 || ((signed char)(info.y) != info.y)
1002 || ((signed char)(info.xOff) != info.xOff));
1005 delete [] glyph_buffer;
1009 int pitch = (format == Format_Mono ? ((info.width + 31) & ~31) >> 3 :
1010 (format == Format_A8 ? (info.width + 3) & ~3 : info.width * 4));
1011 glyph_buffer_size = pitch * info.height;
1012 glyph_buffer = new uchar[glyph_buffer_size];
1014 if (slot->format == FT_GLYPH_FORMAT_OUTLINE) {
1016 bitmap.rows = info.height*vfactor;
1017 bitmap.width = hpixels;
1018 bitmap.pitch = format == Format_Mono ? (((info.width + 31) & ~31) >> 3) : ((bitmap.width + 3) & ~3);
1019 if (!hsubpixel && vfactor == 1)
1020 bitmap.buffer = glyph_buffer;
1022 bitmap.buffer = new uchar[bitmap.rows*bitmap.pitch];
1023 memset(bitmap.buffer, 0, bitmap.rows*bitmap.pitch);
1024 bitmap.pixel_mode = format == Format_Mono ? FT_PIXEL_MODE_MONO : FT_PIXEL_MODE_GRAY;
1026 matrix.xx = (hsubpixel ? 3 : 1) << 16;
1027 matrix.yy = vfactor << 16;
1028 matrix.yx = matrix.xy = 0;
1030 FT_Outline_Transform(&slot->outline, &matrix);
1031 FT_Outline_Translate (&slot->outline, (hsubpixel ? -3*left +(4<<6) : -left), -bottom*vfactor);
1032 FT_Outline_Get_Bitmap(library, &slot->outline, &bitmap);
1034 Q_ASSERT (bitmap.pixel_mode == FT_PIXEL_MODE_GRAY);
1035 Q_ASSERT(antialias);
1036 uchar *convoluted = new uchar[bitmap.rows*bitmap.pitch];
1037 bool useLegacyLcdFilter = false;
1038 #if defined(FC_LCD_FILTER) && defined(FT_LCD_FILTER_H)
1039 useLegacyLcdFilter = (lcdFilterType == FT_LCD_FILTER_LEGACY);
1041 uchar *buffer = bitmap.buffer;
1042 if (!useLegacyLcdFilter) {
1043 convoluteBitmap(bitmap.buffer, convoluted, bitmap.width, info.height, bitmap.pitch);
1044 buffer = convoluted;
1046 convertRGBToARGB(buffer + 1, (uint *)glyph_buffer, info.width, info.height, bitmap.pitch, subpixelType != QFontEngineFT::Subpixel_RGB, useLegacyLcdFilter);
1047 delete [] convoluted;
1048 } else if (vfactor != 1) {
1049 convertRGBToARGB_V(bitmap.buffer, (uint *)glyph_buffer, info.width, info.height, bitmap.pitch, subpixelType != QFontEngineFT::Subpixel_VRGB, true);
1052 if (bitmap.buffer != glyph_buffer)
1053 delete [] bitmap.buffer;
1054 } else if (slot->format == FT_GLYPH_FORMAT_BITMAP) {
1055 Q_ASSERT(slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO);
1056 uchar *src = slot->bitmap.buffer;
1057 uchar *dst = glyph_buffer;
1058 int h = slot->bitmap.rows;
1059 if (format == Format_Mono) {
1060 int bytes = ((info.width + 7) & ~7) >> 3;
1062 memcpy (dst, src, bytes);
1064 src += slot->bitmap.pitch;
1069 uint *dd = (uint *)dst;
1071 for (int x = 0; x < slot->bitmap.width; x++) {
1072 uint a = ((src[x >> 3] & (0x80 >> (x & 7))) ? 0xffffff : 0x000000);
1077 src += slot->bitmap.pitch;
1079 } else if (vfactor != 1) {
1081 uint *dd = (uint *)dst;
1082 for (int x = 0; x < slot->bitmap.width; x++) {
1083 uint a = ((src[x >> 3] & (0x80 >> (x & 7))) ? 0xffffff : 0x000000);
1087 src += slot->bitmap.pitch;
1091 for (int x = 0; x < slot->bitmap.width; x++) {
1092 unsigned char a = ((src[x >> 3] & (0x80 >> (x & 7))) ? 0xff : 0x00);
1096 src += slot->bitmap.pitch;
1101 qWarning("QFontEngine: Glyph neither outline nor bitmap format=%d", slot->format);
1102 delete [] glyph_buffer;
1113 g->linearAdvance = slot->linearHoriAdvance >> 10;
1114 g->width = info.width;
1115 g->height = info.height;
1118 g->advance = info.xOff;
1121 g->data = glyph_buffer;
1124 set->setGlyph(glyph, subPixelPosition, g);
1129 QFontEngine::FaceId QFontEngineFT::faceId() const
1134 QFontEngine::Properties QFontEngineFT::properties() const
1136 Properties p = freetype->properties();
1137 if (p.postscriptName.isEmpty()) {
1138 p.postscriptName = QFontEngine::convertToPostscriptFontFamilyName(fontDef.family.toUtf8());
1141 return freetype->properties();
1144 QFixed QFontEngineFT::emSquareSize() const
1146 if (FT_IS_SCALABLE(freetype->face))
1147 return freetype->face->units_per_EM;
1149 return freetype->face->size->metrics.y_ppem;
1152 bool QFontEngineFT::getSfntTableData(uint tag, uchar *buffer, uint *length) const
1154 return freetype->getSfntTable(tag, buffer, length);
1157 int QFontEngineFT::synthesized() const
1160 if ((fontDef.style != QFont::StyleNormal) && !(freetype->face->style_flags & FT_STYLE_FLAG_ITALIC))
1161 s = SynthesizedItalic;
1162 if ((fontDef.weight == QFont::Bold) && !(freetype->face->style_flags & FT_STYLE_FLAG_BOLD))
1163 s |= SynthesizedBold;
1164 if (fontDef.stretch != 100 && FT_IS_SCALABLE(freetype->face))
1165 s |= SynthesizedStretch;
1169 QFixed QFontEngineFT::ascent() const
1171 return QFixed::fromFixed(metrics.ascender);
1174 QFixed QFontEngineFT::descent() const
1176 // subtract a pixel to work around QFontMetrics's built-in + 1
1177 return QFixed::fromFixed(-metrics.descender - 64);
1180 QFixed QFontEngineFT::leading() const
1182 return QFixed::fromFixed(metrics.height - metrics.ascender + metrics.descender);
1185 QFixed QFontEngineFT::xHeight() const
1187 TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2);
1188 if (os2 && os2->sxHeight) {
1190 QFixed answer = QFixed(os2->sxHeight*freetype->face->size->metrics.y_ppem)/freetype->face->units_per_EM;
1194 return QFontEngine::xHeight();
1197 QFixed QFontEngineFT::averageCharWidth() const
1199 TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2);
1200 if (os2 && os2->xAvgCharWidth) {
1202 QFixed answer = QFixed(os2->xAvgCharWidth*freetype->face->size->metrics.x_ppem)/freetype->face->units_per_EM;
1206 return QFontEngine::averageCharWidth();
1209 qreal QFontEngineFT::maxCharWidth() const
1211 return metrics.max_advance >> 6;
1214 static const ushort char_table[] = {
1236 static const int char_table_entries = sizeof(char_table)/sizeof(ushort);
1239 qreal QFontEngineFT::minLeftBearing() const
1241 if (lbearing == SHRT_MIN)
1242 (void) minRightBearing(); // calculates both
1243 return lbearing.toReal();
1246 qreal QFontEngineFT::minRightBearing() const
1248 if (rbearing == SHRT_MIN) {
1249 lbearing = rbearing = 0;
1250 const QChar *ch = (const QChar *)(const void*)char_table;
1251 QGlyphLayoutArray<char_table_entries> glyphs;
1252 int ng = char_table_entries;
1253 stringToCMap(ch, char_table_entries, &glyphs, &ng, QTextEngine::GlyphIndicesOnly);
1255 if (glyphs.glyphs[ng]) {
1256 glyph_metrics_t gi = const_cast<QFontEngineFT *>(this)->boundingBox(glyphs.glyphs[ng]);
1257 lbearing = qMin(lbearing, gi.x);
1258 rbearing = qMin(rbearing, (gi.xoff - gi.x - gi.width));
1262 return rbearing.toReal();
1265 QFixed QFontEngineFT::lineThickness() const
1267 return line_thickness;
1270 QFixed QFontEngineFT::underlinePosition() const
1272 return underline_position;
1275 void QFontEngineFT::doKerning(QGlyphLayout *g, QTextEngine::ShaperFlags flags) const
1277 if (!kerning_pairs_loaded) {
1278 kerning_pairs_loaded = true;
1280 if (freetype->face->size->metrics.x_ppem != 0) {
1281 QFixed scalingFactor(freetype->face->units_per_EM/freetype->face->size->metrics.x_ppem);
1283 const_cast<QFontEngineFT *>(this)->loadKerningPairs(scalingFactor);
1288 QFontEngine::doKerning(g, flags);
1291 QFontEngineFT::QGlyphSet *QFontEngineFT::loadTransformedGlyphSet(const QTransform &matrix)
1293 if (matrix.type() > QTransform::TxShear)
1296 // FT_Set_Transform only supports scalable fonts
1297 if (!FT_IS_SCALABLE(freetype->face))
1301 m.xx = FT_Fixed(matrix.m11() * 65536);
1302 m.xy = FT_Fixed(-matrix.m21() * 65536);
1303 m.yx = FT_Fixed(-matrix.m12() * 65536);
1304 m.yy = FT_Fixed(matrix.m22() * 65536);
1308 for (int i = 0; i < transformedGlyphSets.count(); ++i) {
1309 const QGlyphSet &g = transformedGlyphSets.at(i);
1310 if (g.transformationMatrix.xx == m.xx
1311 && g.transformationMatrix.xy == m.xy
1312 && g.transformationMatrix.yx == m.yx
1313 && g.transformationMatrix.yy == m.yy) {
1315 // found a match, move it to the front
1316 transformedGlyphSets.move(i, 0);
1317 gs = &transformedGlyphSets[0];
1323 // don't try to load huge fonts
1324 bool draw_as_outline = fontDef.pixelSize * qSqrt(qAbs(matrix.det())) >= QT_MAX_CACHED_GLYPH_SIZE;
1325 if (draw_as_outline)
1328 // don't cache more than 10 transformations
1329 if (transformedGlyphSets.count() >= 10) {
1330 transformedGlyphSets.move(transformedGlyphSets.size() - 1, 0);
1332 transformedGlyphSets.prepend(QGlyphSet());
1334 gs = &transformedGlyphSets[0];
1336 gs->transformationMatrix = m;
1337 gs->outline_drawing = draw_as_outline;
1343 bool QFontEngineFT::loadGlyphs(QGlyphSet *gs, const glyph_t *glyphs, int num_glyphs,
1344 const QFixedPoint *positions,
1349 for (int i = 0; i < num_glyphs; ++i) {
1350 QFixed spp = subPixelPositionForX(positions[i].x);
1351 Glyph *glyph = gs ? gs->getGlyph(glyphs[i], spp) : 0;
1352 if (glyph == 0 || glyph->format != format) {
1355 FT_Matrix m = matrix;
1356 FT_Matrix_Multiply(&gs->transformationMatrix, &m);
1357 FT_Set_Transform(face, &m, 0);
1358 freetype->matrix = m;
1360 if (!loadGlyph(gs, glyphs[i], spp, format)) {
1373 void QFontEngineFT::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics)
1375 FT_Face face = lockFace(Unscaled);
1376 FT_Set_Transform(face, 0, 0);
1377 FT_Load_Glyph(face, glyph, FT_LOAD_NO_BITMAP);
1379 int left = face->glyph->metrics.horiBearingX;
1380 int right = face->glyph->metrics.horiBearingX + face->glyph->metrics.width;
1381 int top = face->glyph->metrics.horiBearingY;
1382 int bottom = face->glyph->metrics.horiBearingY - face->glyph->metrics.height;
1388 metrics->width = QFixed::fromFixed(right-left);
1389 metrics->height = QFixed::fromFixed(top-bottom);
1390 metrics->x = QFixed::fromFixed(left);
1391 metrics->y = QFixed::fromFixed(-top);
1392 metrics->xoff = QFixed::fromFixed(face->glyph->advance.x);
1394 if (!FT_IS_SCALABLE(freetype->face))
1395 QFreetypeFace::addBitmapToPath(face->glyph, p, path);
1397 QFreetypeFace::addGlyphToPath(face, face->glyph, p, path, face->units_per_EM << 6, face->units_per_EM << 6);
1399 FT_Set_Transform(face, &freetype->matrix, 0);
1403 static inline unsigned int getChar(const QChar *str, int &i, const int len)
1405 uint ucs4 = str[i].unicode();
1406 if (str[i].isHighSurrogate() && i < len-1 && str[i+1].isLowSurrogate()) {
1408 ucs4 = QChar::surrogateToUcs4(ucs4, str[i].unicode());
1413 bool QFontEngineFT::canRender(const QChar *string, int len)
1415 FT_Face face = freetype->face;
1419 for ( int i = 0; i < len; i++ ) {
1420 unsigned int uc = getChar(string, i, len);
1421 if (!FcCharSetHasChar (_font->charset, uc) && getAdobeCharIndex(face, _cmap, uc) == 0) {
1430 for ( int i = 0; i < len; i++ ) {
1431 unsigned int uc = getChar(string, i, len);
1432 if (!FT_Get_Char_Index(face, uc))
1439 void QFontEngineFT::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags)
1441 if (!glyphs.numGlyphs)
1444 if (FT_IS_SCALABLE(freetype->face)) {
1445 QFontEngine::addOutlineToPath(x, y, glyphs, path, flags);
1447 QVarLengthArray<QFixedPoint> positions;
1448 QVarLengthArray<glyph_t> positioned_glyphs;
1450 matrix.translate(x, y);
1451 getGlyphPositions(glyphs, matrix, flags, positioned_glyphs, positions);
1453 FT_Face face = lockFace(Unscaled);
1454 for (int gl = 0; gl < glyphs.numGlyphs; gl++) {
1455 FT_UInt glyph = positioned_glyphs[gl];
1456 FT_Load_Glyph(face, glyph, FT_LOAD_TARGET_MONO);
1457 freetype->addBitmapToPath(face->glyph, positions[gl], path);
1463 void QFontEngineFT::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int numGlyphs,
1464 QPainterPath *path, QTextItem::RenderFlags)
1466 FT_Face face = lockFace(Unscaled);
1468 for (int gl = 0; gl < numGlyphs; gl++) {
1469 FT_UInt glyph = glyphs[gl];
1471 FT_Load_Glyph(face, glyph, FT_LOAD_NO_BITMAP);
1473 FT_GlyphSlot g = face->glyph;
1474 if (g->format != FT_GLYPH_FORMAT_OUTLINE)
1476 if (embolden) Q_FT_GLYPHSLOT_EMBOLDEN(g);
1477 if (obliquen) Q_FT_GLYPHSLOT_OBLIQUE(g);
1478 QFreetypeFace::addGlyphToPath(face, g, positions[gl], path, xsize, ysize);
1483 bool QFontEngineFT::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs,
1484 QTextEngine::ShaperFlags flags) const
1486 if (*nglyphs < len) {
1491 bool mirrored = flags & QTextEngine::RightToLeft;
1493 if (freetype->symbol_map) {
1494 FT_Face face = freetype->face;
1495 for ( int i = 0; i < len; ++i ) {
1496 unsigned int uc = getChar(str, i, len);
1497 glyphs->glyphs[glyph_pos] = uc < QFreetypeFace::cmapCacheSize ? freetype->cmapCache[uc] : 0;
1498 if ( !glyphs->glyphs[glyph_pos] ) {
1500 #if !defined(QT_NO_FONTCONFIG)
1501 if (freetype->charset != 0 && FcCharSetHasChar(freetype->charset, uc)) {
1506 glyph = FT_Get_Char_Index(face, uc);
1507 if (!glyph && (uc == 0xa0 || uc == 0x9)) {
1512 FT_Set_Charmap(face, freetype->symbol_map);
1513 glyph = FT_Get_Char_Index(face, uc);
1514 FT_Set_Charmap(face, freetype->unicode_map);
1516 glyphs->glyphs[glyph_pos] = glyph;
1517 if (uc < QFreetypeFace::cmapCacheSize)
1518 freetype->cmapCache[uc] = glyph;
1523 FT_Face face = freetype->face;
1524 for (int i = 0; i < len; ++i) {
1525 unsigned int uc = getChar(str, i, len);
1527 uc = QChar::mirroredChar(uc);
1528 glyphs->glyphs[glyph_pos] = uc < QFreetypeFace::cmapCacheSize ? freetype->cmapCache[uc] : 0;
1529 if (!glyphs->glyphs[glyph_pos]) {
1530 #if !defined(QT_NO_FONTCONFIG)
1531 if (freetype->charset == 0 || FcCharSetHasChar(freetype->charset, uc))
1535 glyph_t glyph = FT_Get_Char_Index(face, uc);
1536 if (!glyph && (uc == 0xa0 || uc == 0x9)) {
1540 glyphs->glyphs[glyph_pos] = glyph;
1541 if (uc < QFreetypeFace::cmapCacheSize)
1542 freetype->cmapCache[uc] = glyph;
1549 *nglyphs = glyph_pos;
1550 glyphs->numGlyphs = glyph_pos;
1552 if (flags & QTextEngine::GlyphIndicesOnly)
1555 recalcAdvances(glyphs, flags);
1560 void QFontEngineFT::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const
1563 bool design = (default_hint_style == HintNone ||
1564 default_hint_style == HintLight ||
1565 (flags & HB_ShaperFlag_UseDesignMetrics)) && FT_IS_SCALABLE(freetype->face);
1566 for (int i = 0; i < glyphs->numGlyphs; i++) {
1567 Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(glyphs->glyphs[i]) : 0;
1569 glyphs->advances_x[i] = design ? QFixed::fromFixed(g->linearAdvance) : QFixed(g->advance);
1573 g = loadGlyph(cacheEnabled ? &defaultGlyphSet : 0, glyphs->glyphs[i], 0, Format_None, true);
1574 glyphs->advances_x[i] = design ? QFixed::fromFixed(face->glyph->linearHoriAdvance >> 10)
1575 : QFixed::fromFixed(face->glyph->metrics.horiAdvance).round();
1577 if (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
1578 glyphs->advances_x[i] = glyphs->advances_x[i].round();
1579 glyphs->advances_y[i] = 0;
1585 glyph_metrics_t QFontEngineFT::boundingBox(const QGlyphLayout &glyphs)
1589 glyph_metrics_t overall;
1590 // initialize with line height, we get the same behaviour on all platforms
1591 overall.y = -ascent();
1592 overall.height = ascent() + descent() + 1;
1596 for (int i = 0; i < glyphs.numGlyphs; i++) {
1597 Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(glyphs.glyphs[i]) : 0;
1601 g = loadGlyph(cacheEnabled ? &defaultGlyphSet : 0, glyphs.glyphs[i], 0, Format_None, true);
1604 QFixed x = overall.xoff + glyphs.offsets[i].x + g->x;
1605 QFixed y = overall.yoff + glyphs.offsets[i].y - g->y;
1606 overall.x = qMin(overall.x, x);
1607 overall.y = qMin(overall.y, y);
1608 xmax = qMax(xmax, x + g->width);
1609 ymax = qMax(ymax, y + g->height);
1610 overall.xoff += g->advance;
1612 int left = FLOOR(face->glyph->metrics.horiBearingX);
1613 int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width);
1614 int top = CEIL(face->glyph->metrics.horiBearingY);
1615 int bottom = FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height);
1617 QFixed x = overall.xoff + glyphs.offsets[i].x - (-TRUNC(left));
1618 QFixed y = overall.yoff + glyphs.offsets[i].y - TRUNC(top);
1619 overall.x = qMin(overall.x, x);
1620 overall.y = qMin(overall.y, y);
1621 xmax = qMax(xmax, x + TRUNC(right - left));
1622 ymax = qMax(ymax, y + TRUNC(top - bottom));
1623 overall.xoff += int(TRUNC(ROUND(face->glyph->advance.x)));
1626 overall.height = qMax(overall.height, ymax - overall.y);
1627 overall.width = xmax - overall.x;
1635 glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph)
1638 glyph_metrics_t overall;
1639 Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(glyph) : 0;
1642 g = loadGlyph(cacheEnabled ? &defaultGlyphSet : 0, glyph, 0, Format_None, true);
1647 overall.width = g->width;
1648 overall.height = g->height;
1649 overall.xoff = g->advance;
1650 if (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
1651 overall.xoff = overall.xoff.round();
1653 int left = FLOOR(face->glyph->metrics.horiBearingX);
1654 int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width);
1655 int top = CEIL(face->glyph->metrics.horiBearingY);
1656 int bottom = FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height);
1658 overall.width = TRUNC(right-left);
1659 overall.height = TRUNC(top-bottom);
1660 overall.x = TRUNC(left);
1661 overall.y = -TRUNC(top);
1662 overall.xoff = TRUNC(ROUND(face->glyph->advance.x));
1669 glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph, const QTransform &matrix)
1671 return alphaMapBoundingBox(glyph, 0, matrix, QFontEngine::Format_None);
1674 static FT_Matrix QTransformToFTMatrix(const QTransform &matrix)
1678 m.xx = FT_Fixed(matrix.m11() * 65536);
1679 m.xy = FT_Fixed(-matrix.m21() * 65536);
1680 m.yx = FT_Fixed(-matrix.m12() * 65536);
1681 m.yy = FT_Fixed(matrix.m22() * 65536);
1686 glyph_metrics_t QFontEngineFT::alphaMapBoundingBox(glyph_t glyph, QFixed subPixelPosition, const QTransform &matrix, QFontEngine::GlyphFormat format)
1689 glyph_metrics_t overall;
1690 QGlyphSet *glyphSet = 0;
1691 FT_Matrix ftMatrix = QTransformToFTMatrix(matrix);
1693 if (matrix.type() > QTransform::TxTranslate && FT_IS_SCALABLE(freetype->face)) {
1694 // TODO move everything here to a method of its own to access glyphSets
1695 // to be shared with a new method that will replace loadTransformedGlyphSet()
1696 for (int i = 0; i < transformedGlyphSets.count(); ++i) {
1697 const QGlyphSet &g = transformedGlyphSets.at(i);
1698 if (g.transformationMatrix.xx == ftMatrix.xx
1699 && g.transformationMatrix.xy == ftMatrix.xy
1700 && g.transformationMatrix.yx == ftMatrix.yx
1701 && g.transformationMatrix.yy == ftMatrix.yy) {
1703 // found a match, move it to the front
1704 transformedGlyphSets.move(i, 0);
1705 glyphSet = &transformedGlyphSets[0];
1711 // don't cache more than 10 transformations
1712 if (transformedGlyphSets.count() >= 10) {
1713 transformedGlyphSets.move(transformedGlyphSets.size() - 1, 0);
1715 transformedGlyphSets.prepend(QGlyphSet());
1717 glyphSet = &transformedGlyphSets[0];
1719 glyphSet->transformationMatrix = ftMatrix;
1723 glyphSet = &defaultGlyphSet;
1726 Glyph * g = glyphSet ? glyphSet->getGlyph(glyph) : 0;
1727 if (!g || g->format != format) {
1729 FT_Matrix m = this->matrix;
1730 FT_Matrix_Multiply(&ftMatrix, &m);
1731 freetype->matrix = m;
1732 g = loadGlyph(glyphSet, glyph, subPixelPosition, format, false);
1738 overall.width = g->width;
1739 overall.height = g->height;
1740 overall.xoff = g->advance;
1742 int left = FLOOR(face->glyph->metrics.horiBearingX);
1743 int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width);
1744 int top = CEIL(face->glyph->metrics.horiBearingY);
1745 int bottom = FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height);
1747 overall.width = TRUNC(right-left);
1748 overall.height = TRUNC(top-bottom);
1749 overall.x = TRUNC(left);
1750 overall.y = -TRUNC(top);
1751 overall.xoff = TRUNC(ROUND(face->glyph->advance.x));
1758 QImage *QFontEngineFT::lockedAlphaMapForGlyph(glyph_t glyphIndex, QFixed subPixelPosition,
1759 QFontEngine::GlyphFormat neededFormat,
1760 const QTransform &t, QPoint *offset)
1762 Q_ASSERT(currentlyLockedAlphaMap.isNull());
1766 neededFormat = Format_Mono;
1767 else if (neededFormat == Format_None && defaultFormat != Format_None)
1768 neededFormat = defaultFormat;
1769 else if (neededFormat == Format_None)
1770 neededFormat = Format_A8;
1772 QImage::Format format;
1773 switch (neededFormat) {
1775 format = QImage::Format_Mono;
1778 format = QImage::Format_Indexed8;
1781 format = QImage::Format_ARGB32;
1785 format = QImage::Format_Invalid;
1788 QFontEngineFT::Glyph *glyph;
1790 QFontEngineFT::QGlyphSet *gset = &defaultGlyphSet;
1791 if (t.type() >= QTransform::TxScale) {
1793 gset = loadTransformedGlyphSet(t);
1799 FT_Matrix m = matrix;
1800 FT_Matrix_Multiply(&gset->transformationMatrix, &m);
1801 FT_Set_Transform(freetype->face, &m, 0);
1802 freetype->matrix = m;
1805 if (!gset || gset->outline_drawing || !loadGlyph(gset, glyphIndex, subPixelPosition,
1807 return QFontEngine::lockedAlphaMapForGlyph(glyphIndex, subPixelPosition, neededFormat, t,
1811 glyph = gset->getGlyph(glyphIndex, subPixelPosition);
1813 FT_Matrix m = matrix;
1814 FT_Matrix extra = QTransformToFTMatrix(t);
1815 FT_Matrix_Multiply(&extra, &m);
1816 FT_Set_Transform(freetype->face, &m, 0);
1817 freetype->matrix = m;
1818 glyph = loadGlyph(0, glyphIndex, subPixelPosition, neededFormat);
1821 if (glyph == 0 || glyph->data == 0 || glyph->width == 0 || glyph->height == 0) {
1827 switch (neededFormat) {
1829 pitch = ((glyph->width + 31) & ~31) >> 3;
1832 pitch = (glyph->width + 3) & ~3;
1835 pitch = glyph->width * 4;
1843 *offset = QPoint(glyph->x, -glyph->y);
1845 currentlyLockedAlphaMap = QImage(glyph->data, glyph->width, glyph->height, pitch, format);
1846 Q_ASSERT(!currentlyLockedAlphaMap.isNull());
1848 QImageData *data = currentlyLockedAlphaMap.data_ptr();
1849 data->is_locked = true;
1851 return ¤tlyLockedAlphaMap;
1854 void QFontEngineFT::unlockAlphaMapForGlyph()
1856 Q_ASSERT(!currentlyLockedAlphaMap.isNull());
1858 currentlyLockedAlphaMap = QImage();
1861 QFontEngineFT::Glyph *QFontEngineFT::loadGlyphFor(glyph_t g, QFixed subPixelPosition, GlyphFormat format)
1863 return defaultGlyphSet.outline_drawing ? 0 :
1864 loadGlyph(cacheEnabled ? &defaultGlyphSet : 0, g, subPixelPosition, format);
1867 QImage QFontEngineFT::alphaMapForGlyph(glyph_t g, QFixed subPixelPosition)
1871 Glyph *glyph = loadGlyphFor(g, subPixelPosition, antialias ? Format_A8 : Format_Mono);
1874 return QFontEngine::alphaMapForGlyph(g);
1877 const int pitch = antialias ? (glyph->width + 3) & ~3 : ((glyph->width + 31)/32) * 4;
1879 QImage img(glyph->width, glyph->height, antialias ? QImage::Format_Indexed8 : QImage::Format_Mono);
1881 QVector<QRgb> colors(256);
1882 for (int i=0; i<256; ++i)
1883 colors[i] = qRgba(0, 0, 0, i);
1884 img.setColorTable(colors);
1886 QVector<QRgb> colors(2);
1887 colors[0] = qRgba(0, 0, 0, 0);
1888 colors[1] = qRgba(0, 0, 0, 255);
1889 img.setColorTable(colors);
1891 Q_ASSERT(img.bytesPerLine() == pitch);
1893 for (int y = 0; y < glyph->height; ++y)
1894 memcpy(img.scanLine(y), &glyph->data[y * pitch], pitch);
1901 QImage QFontEngineFT::alphaRGBMapForGlyph(glyph_t g, QFixed subPixelPosition, const QTransform &t)
1903 if (t.type() > QTransform::TxTranslate)
1904 return QFontEngine::alphaRGBMapForGlyph(g, subPixelPosition, t);
1908 Glyph *glyph = loadGlyphFor(g, subPixelPosition, Format_A32);
1911 return QFontEngine::alphaRGBMapForGlyph(g, subPixelPosition, t);
1914 QImage img(glyph->width, glyph->height, QImage::Format_RGB32);
1915 memcpy(img.bits(), glyph->data, 4 * glyph->width * glyph->height);
1921 void QFontEngineFT::removeGlyphFromCache(glyph_t glyph)
1923 defaultGlyphSet.removeGlyphFromCache(glyph, 0);
1926 int QFontEngineFT::glyphCount() const
1929 FT_Face face = lockFace();
1931 count = face->num_glyphs;
1937 FT_Face QFontEngineFT::lockFace(Scaling scale) const
1940 FT_Face face = freetype->face;
1941 if (scale == Unscaled) {
1942 FT_Set_Char_Size(face, face->units_per_EM << 6, face->units_per_EM << 6, 0, 0);
1943 freetype->xsize = face->units_per_EM << 6;
1944 freetype->ysize = face->units_per_EM << 6;
1945 } else if (freetype->xsize != xsize || freetype->ysize != ysize) {
1946 FT_Set_Char_Size(face, xsize, ysize, 0, 0);
1947 freetype->xsize = xsize;
1948 freetype->ysize = ysize;
1950 if (freetype->matrix.xx != matrix.xx ||
1951 freetype->matrix.yy != matrix.yy ||
1952 freetype->matrix.xy != matrix.xy ||
1953 freetype->matrix.yx != matrix.yx) {
1954 freetype->matrix = matrix;
1955 FT_Set_Transform(face, &freetype->matrix, 0);
1961 void QFontEngineFT::unlockFace() const
1966 FT_Face QFontEngineFT::non_locked_face() const
1968 return freetype->face;
1972 QFontEngineFT::QGlyphSet::QGlyphSet()
1973 : outline_drawing(false)
1975 transformationMatrix.xx = 0x10000;
1976 transformationMatrix.yy = 0x10000;
1977 transformationMatrix.xy = 0;
1978 transformationMatrix.yx = 0;
1979 memset(fast_glyph_data, 0, sizeof(fast_glyph_data));
1980 fast_glyph_count = 0;
1983 QFontEngineFT::QGlyphSet::~QGlyphSet()
1988 void QFontEngineFT::QGlyphSet::clear()
1990 if (fast_glyph_count > 0) {
1991 for (int i = 0; i < 256; ++i) {
1992 if (fast_glyph_data[i]) {
1993 delete fast_glyph_data[i];
1994 fast_glyph_data[i] = 0;
1997 fast_glyph_count = 0;
1999 qDeleteAll(glyph_data);
2003 void QFontEngineFT::QGlyphSet::removeGlyphFromCache(glyph_t index, QFixed subPixelPosition)
2005 if (useFastGlyphData(index, subPixelPosition)) {
2006 if (fast_glyph_data[index]) {
2007 delete fast_glyph_data[index];
2008 fast_glyph_data[index] = 0;
2009 if (fast_glyph_count > 0)
2013 delete glyph_data.take(GlyphAndSubPixelPosition(index, subPixelPosition));
2017 void QFontEngineFT::QGlyphSet::setGlyph(glyph_t index, QFixed subPixelPosition, Glyph *glyph)
2019 if (useFastGlyphData(index, subPixelPosition)) {
2020 if (!fast_glyph_data[index])
2022 fast_glyph_data[index] = glyph;
2024 glyph_data.insert(GlyphAndSubPixelPosition(index, subPixelPosition), glyph);
2028 HB_Error QFontEngineFT::getPointInOutline(HB_Glyph glyph, int flags, hb_uint32 point, HB_Fixed *xpos, HB_Fixed *ypos, hb_uint32 *nPoints)
2031 bool hsubpixel = true;
2033 int load_flags = loadFlags(0, Format_A8, flags, hsubpixel, vfactor);
2034 HB_Error result = freetype->getPointInOutline(glyph, load_flags, point, xpos, ypos, nPoints);
2039 bool QFontEngineFT::initFromFontEngine(const QFontEngineFT *fe)
2041 if (!init(fe->faceId(), fe->antialias, fe->defaultFormat, fe->freetype))
2044 // Increase the reference of this QFreetypeFace since one more QFontEngineFT
2046 freetype->ref.ref();
2048 default_load_flags = fe->default_load_flags;
2049 default_hint_style = fe->default_hint_style;
2050 antialias = fe->antialias;
2051 transform = fe->transform;
2052 embolden = fe->embolden;
2053 obliquen = fe->obliquen;
2054 subpixelType = fe->subpixelType;
2055 lcdFilterType = fe->lcdFilterType;
2056 embeddedbitmap = fe->embeddedbitmap;
2061 QFontEngine *QFontEngineFT::cloneWithSize(qreal pixelSize) const
2063 QFontDef fontDef(this->fontDef);
2064 fontDef.pixelSize = pixelSize;
2065 QFontEngineFT *fe = new QFontEngineFT(fontDef);
2066 if (!fe->initFromFontEngine(this)) {
2076 #endif // QT_NO_FREETYPE