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 "qabstractfileengine.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 QFile file(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 (!(file.fileEngine()->fileFlags(QAbstractFileEngine::FlagsMask) & QAbstractFileEngine::LocalDiskFlag)) {
244 if (!file.open(QIODevice::ReadOnly)) {
247 newFreetype->fontData = file.readAll();
250 newFreetype->fontData = fontData;
252 if (!newFreetype->fontData.isEmpty()) {
253 if (FT_New_Memory_Face(freetypeData->library, (const FT_Byte *)newFreetype->fontData.constData(), newFreetype->fontData.size(), face_id.index, &face)) {
256 } else if (FT_New_Face(freetypeData->library, face_id.filename, face_id.index, &face)) {
259 newFreetype->face = face;
261 newFreetype->hbFace = qHBNewFace(face, hb_getSFntTable);
262 Q_CHECK_PTR(newFreetype->hbFace);
263 newFreetype->ref.store(1);
264 newFreetype->xsize = 0;
265 newFreetype->ysize = 0;
266 newFreetype->matrix.xx = 0x10000;
267 newFreetype->matrix.yy = 0x10000;
268 newFreetype->matrix.xy = 0;
269 newFreetype->matrix.yx = 0;
270 newFreetype->unicode_map = 0;
271 newFreetype->symbol_map = 0;
272 #ifndef QT_NO_FONTCONFIG
273 newFreetype->charset = 0;
276 memset(newFreetype->cmapCache, 0, sizeof(newFreetype->cmapCache));
278 for (int i = 0; i < newFreetype->face->num_charmaps; ++i) {
279 FT_CharMap cm = newFreetype->face->charmaps[i];
280 switch(cm->encoding) {
281 case FT_ENCODING_UNICODE:
282 newFreetype->unicode_map = cm;
284 case FT_ENCODING_APPLE_ROMAN:
285 case FT_ENCODING_ADOBE_LATIN_1:
286 if (!newFreetype->unicode_map || newFreetype->unicode_map->encoding != FT_ENCODING_UNICODE)
287 newFreetype->unicode_map = cm;
289 case FT_ENCODING_ADOBE_CUSTOM:
290 case FT_ENCODING_MS_SYMBOL:
291 if (!newFreetype->symbol_map)
292 newFreetype->symbol_map = cm;
299 if (!FT_IS_SCALABLE(newFreetype->face) && newFreetype->face->num_fixed_sizes == 1)
300 FT_Set_Char_Size (face, X_SIZE(newFreetype->face, 0), Y_SIZE(newFreetype->face, 0), 0, 0);
303 FcPatternGetString(pattern, FC_FAMILY, 0, &name);
304 qDebug("%s: using maps: default: %x unicode: %x, symbol: %x", name,
305 newFreetype->face->charmap ? newFreetype->face->charmap->encoding : 0,
306 newFreetype->unicode_map ? newFreetype->unicode_map->encoding : 0,
307 newFreetype->symbol_map ? newFreetype->symbol_map->encoding : 0);
309 for (int i = 0; i < 256; i += 8)
310 qDebug(" %x: %d %d %d %d %d %d %d %d", i,
311 FcCharSetHasChar(newFreetype->charset, i), FcCharSetHasChar(newFreetype->charset, 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));
317 FT_Set_Charmap(newFreetype->face, newFreetype->unicode_map);
319 freetypeData->faces.insert(face_id, newFreetype.data());
321 newFreetype.take()->release(face_id);
322 // we could return null in principle instead of throwing
325 freetype = newFreetype.take();
330 void QFreetypeFace::release(const QFontEngine::FaceId &face_id)
332 QtFreetypeData *freetypeData = qt_getFreetypeData();
336 #ifndef QT_NO_FONTCONFIG
338 FcCharSetDestroy(charset);
340 if(freetypeData->faces.contains(face_id))
341 freetypeData->faces.take(face_id);
344 if (freetypeData->faces.isEmpty()) {
345 FT_Done_FreeType(freetypeData->library);
346 freetypeData->library = 0;
351 void QFreetypeFace::computeSize(const QFontDef &fontDef, int *xsize, int *ysize, bool *outline_drawing)
353 *ysize = qRound(fontDef.pixelSize * 64);
354 *xsize = *ysize * fontDef.stretch / 100;
355 *outline_drawing = false;
358 * Bitmap only faces must match exactly, so find the closest
359 * one (height dominant search)
361 if (!(face->face_flags & FT_FACE_FLAG_SCALABLE)) {
363 for (int i = 1; i < face->num_fixed_sizes; i++) {
364 if (qAbs(*ysize - Y_SIZE(face,i)) <
365 qAbs (*ysize - Y_SIZE(face, best)) ||
366 (qAbs (*ysize - Y_SIZE(face, i)) ==
367 qAbs (*ysize - Y_SIZE(face, best)) &&
368 qAbs (*xsize - X_SIZE(face, i)) <
369 qAbs (*xsize - X_SIZE(face, best)))) {
373 if (FT_Set_Char_Size (face, X_SIZE(face, best), Y_SIZE(face, best), 0, 0) == 0) {
374 *xsize = X_SIZE(face, best);
375 *ysize = Y_SIZE(face, best);
378 if (!(face->face_flags & FT_FACE_FLAG_SCALABLE) && ysize == 0 && face->num_fixed_sizes >= 1) {
379 // work around FT 2.1.10 problem with BDF without PIXEL_SIZE property
380 err = FT_Set_Pixel_Sizes(face, face->available_sizes[0].width, face->available_sizes[0].height);
381 if (err && face->num_fixed_sizes == 1)
382 err = 0; //even more of a workaround...
389 *outline_drawing = (*xsize > (QT_MAX_CACHED_GLYPH_SIZE<<6) || *ysize > (QT_MAX_CACHED_GLYPH_SIZE<<6));
393 QFontEngine::Properties QFreetypeFace::properties() const
395 QFontEngine::Properties p;
396 p.postscriptName = FT_Get_Postscript_Name(face);
397 PS_FontInfoRec font_info;
398 if (FT_Get_PS_Font_Info(face, &font_info) == 0)
399 p.copyright = font_info.notice;
400 if (FT_IS_SCALABLE(face)) {
401 p.ascent = face->ascender;
402 p.descent = -face->descender;
403 p.leading = face->height - face->ascender + face->descender;
404 p.emSquare = face->units_per_EM;
405 p.boundingBox = QRectF(face->bbox.xMin, -face->bbox.yMax,
406 face->bbox.xMax - face->bbox.xMin,
407 face->bbox.yMax - face->bbox.yMin);
409 p.ascent = QFixed::fromFixed(face->size->metrics.ascender);
410 p.descent = QFixed::fromFixed(-face->size->metrics.descender);
411 p.leading = QFixed::fromFixed(face->size->metrics.height - face->size->metrics.ascender + face->size->metrics.descender);
412 p.emSquare = face->size->metrics.y_ppem;
413 // p.boundingBox = QRectF(-p.ascent.toReal(), 0, (p.ascent + p.descent).toReal(), face->size->metrics.max_advance/64.);
414 p.boundingBox = QRectF(0, -p.ascent.toReal(),
415 face->size->metrics.max_advance/64, (p.ascent + p.descent).toReal() );
418 p.capHeight = p.ascent;
419 p.lineWidth = face->underline_thickness;
423 bool QFreetypeFace::getSfntTable(uint tag, uchar *buffer, uint *length) const
426 #if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) > 20103
427 if (FT_IS_SFNT(face)) {
428 FT_ULong len = *length;
429 result = FT_Load_Sfnt_Table(face, tag, 0, buffer, &len) == FT_Err_Ok;
436 /* Some fonts (such as MingLiu rely on hinting to scale different
437 components to their correct sizes. While this is really broken (it
438 should be done in the component glyph itself, not the hinter) we
439 will have to live with it.
441 This means we can not use FT_LOAD_NO_HINTING to get the glyph
442 outline. All we can do is to load the unscaled glyph and scale it
443 down manually when required.
445 static void scaleOutline(FT_Face face, FT_GlyphSlot g, FT_Fixed x_scale, FT_Fixed y_scale)
447 x_scale = FT_MulDiv(x_scale, 1 << 10, face->units_per_EM);
448 y_scale = FT_MulDiv(y_scale, 1 << 10, face->units_per_EM);
449 FT_Vector *p = g->outline.points;
450 const FT_Vector *e = p + g->outline.n_points;
452 p->x = FT_MulFix(p->x, x_scale);
453 p->y = FT_MulFix(p->y, y_scale);
458 void QFreetypeFace::addGlyphToPath(FT_Face face, FT_GlyphSlot g, const QFixedPoint &point, QPainterPath *path, FT_Fixed x_scale, FT_Fixed y_scale)
460 const qreal factor = 1/64.;
461 scaleOutline(face, g, x_scale, y_scale);
463 QPointF cp = point.toPointF();
465 // convert the outline to a painter path
467 for (int j = 0; j < g->outline.n_contours; ++j) {
468 int last_point = g->outline.contours[j];
469 QPointF start = cp + QPointF(g->outline.points[i].x*factor, -g->outline.points[i].y*factor);
470 if(!(g->outline.tags[i] & 1)) {
471 start += cp + QPointF(g->outline.points[last_point].x*factor, -g->outline.points[last_point].y*factor);
474 // qDebug("contour: %d -- %d", i, g->outline.contours[j]);
475 // qDebug("first point at %f %f", start.x(), start.y());
481 while (i < last_point) {
483 c[n] = cp + QPointF(g->outline.points[i].x*factor, -g->outline.points[i].y*factor);
484 // qDebug() << " i=" << i << " flag=" << (int)g->outline.tags[i] << "point=" << c[n];
486 switch (g->outline.tags[i] & 3) {
488 // cubic bezier element
491 c[3] = (c[3] + c[2])/2;
495 // quadratic bezier element
498 c[3] = (c[1] + c[2])/2;
499 c[2] = (2*c[1] + c[3])/3;
500 c[1] = (2*c[1] + c[0])/3;
506 // qDebug() << "lineTo" << c[1];
513 c[2] = (2*c[1] + c[3])/3;
514 c[1] = (2*c[1] + c[0])/3;
518 // qDebug() << "cubicTo" << c[1] << c[2] << c[3];
519 path->cubicTo(c[1], c[2], c[3]);
524 // qDebug() << "closeSubpath";
525 path->closeSubpath();
529 c[2] = (2*c[1] + c[3])/3;
530 c[1] = (2*c[1] + c[0])/3;
532 // qDebug() << "cubicTo" << c[1] << c[2] << c[3];
533 path->cubicTo(c[1], c[2], c[3]);
539 extern void qt_addBitmapToPath(qreal x0, qreal y0, const uchar *image_data, int bpl, int w, int h, QPainterPath *path);
541 void QFreetypeFace::addBitmapToPath(FT_GlyphSlot slot, const QFixedPoint &point, QPainterPath *path, bool)
543 if (slot->format != FT_GLYPH_FORMAT_BITMAP
544 || slot->bitmap.pixel_mode != FT_PIXEL_MODE_MONO)
547 QPointF cp = point.toPointF();
548 qt_addBitmapToPath(cp.x() + TRUNC(slot->metrics.horiBearingX), cp.y() - TRUNC(slot->metrics.horiBearingY),
549 slot->bitmap.buffer, slot->bitmap.pitch, slot->bitmap.width, slot->bitmap.rows, path);
552 QFontEngineFT::Glyph::~Glyph()
557 static const uint subpixel_filter[3][3] = {
563 static inline uint filterPixel(uint red, uint green, uint blue, bool legacyFilter)
567 uint high = (red*subpixel_filter[0][0] + green*subpixel_filter[0][1] + blue*subpixel_filter[0][2]) >> 8;
568 uint mid = (red*subpixel_filter[1][0] + green*subpixel_filter[1][1] + blue*subpixel_filter[1][2]) >> 8;
569 uint low = (red*subpixel_filter[2][0] + green*subpixel_filter[2][1] + blue*subpixel_filter[2][2]) >> 8;
570 res = (mid << 24) + (high << 16) + (mid << 8) + low;
573 res = (alpha << 24) + (red << 16) + (green << 8) + blue;
578 static void convertRGBToARGB(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr, bool legacyFilter)
581 const int offs = bgr ? -1 : 1;
582 const int w = width * 3;
585 for (int x = 0; x < w; x += 3) {
586 uint red = src[x+1-offs];
587 uint green = src[x+1];
588 uint blue = src[x+1+offs];
589 *dd = filterPixel(red, green, blue, legacyFilter);
597 static void convertRGBToARGB_V(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr, bool legacyFilter)
600 const int offs = bgr ? -src_pitch : src_pitch;
602 for (int x = 0; x < width; x++) {
603 uint red = src[x+src_pitch-offs];
604 uint green = src[x+src_pitch];
605 uint blue = src[x+src_pitch+offs];
606 dst[x] = filterPixel(red, green, blue, legacyFilter);
613 static void convoluteBitmap(const uchar *src, uchar *dst, int width, int height, int pitch)
615 // convolute the bitmap with a triangle filter to get rid of color fringes
616 // If we take account for a gamma value of 2, we end up with
617 // weights of 1, 4, 9, 4, 1. We use an approximation of 1, 3, 8, 3, 1 here,
618 // as this nicely sums up to 16 :)
623 for (int x = 2; x < width - 2; ++x) {
624 uint sum = src[x-2] + 3*src[x-1] + 8*src[x] + 3*src[x+1] + src[x+2];
625 dst[x] = (uchar) (sum >> 4);
627 dst[width - 2] = dst[width - 1] = 0;
633 QFontEngineFT::QFontEngineFT(const QFontDef &fd)
641 kerning_pairs_loaded = false;
647 default_load_flags = FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
649 default_hint_style = HintNone;
651 default_hint_style = HintFull;
653 subpixelType = Subpixel_None;
655 #if defined(FT_LCD_FILTER_H)
656 lcdFilterType = (int)((quintptr) FT_LCD_FILTER_DEFAULT);
658 defaultFormat = Format_None;
659 embeddedbitmap = false;
660 cacheEnabled = qgetenv("QT_NO_FT_CACHE").isEmpty() || qgetenv("QT_NO_FT_CACHE").toInt() == 0;
661 m_subPixelPositionCount = 4;
664 QFontEngineFT::~QFontEngineFT()
667 freetype->release(face_id);
668 hbFace = 0; // we share the face in QFreeTypeFace, don't let ~QFontEngine delete it
671 bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format,
672 const QByteArray &fontData)
674 return init(faceId, antialias, format, QFreetypeFace::getFace(faceId, fontData));
677 bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format,
678 QFreetypeFace *freetypeFace)
680 freetype = freetypeFace;
686 defaultFormat = format;
687 this->antialias = antialias;
690 glyphFormat = QFontEngineGlyphCache::Raster_Mono;
691 else if (format == Format_A8)
692 glyphFormat = QFontEngineGlyphCache::Raster_A8;
693 else if (format == Format_A32)
694 glyphFormat = QFontEngineGlyphCache::Raster_RGBMask;
698 symbol = freetype->symbol_map != 0;
699 PS_FontInfoRec psrec;
700 // don't assume that type1 fonts are symbol fonts by default
701 if (FT_Get_PS_Font_Info(freetype->face, &psrec) == FT_Err_Ok) {
702 symbol = bool(fontDef.family.contains(QLatin1String("symbol"), Qt::CaseInsensitive));
705 freetype->hbFace->isSymbolFont = symbol;
707 lbearing = rbearing = SHRT_MIN;
708 freetype->computeSize(fontDef, &xsize, &ysize, &defaultGlyphSet.outline_drawing);
710 FT_Face face = lockFace();
712 if (FT_IS_SCALABLE(face)) {
713 bool fake_oblique = (fontDef.style != QFont::StyleNormal) && !(face->style_flags & FT_STYLE_FLAG_ITALIC);
715 #if !defined(Q_HAS_FT_GLYPHSLOT_OBLIQUE)
716 matrix.xy = 0x10000*3/10;
722 FT_Set_Transform(face, &matrix, 0);
723 freetype->matrix = matrix;
725 if ((fontDef.weight == QFont::Bold) && !(face->style_flags & FT_STYLE_FLAG_BOLD) && !FT_IS_FIXED_WIDTH(face))
728 line_thickness = QFixed::fromFixed(FT_MulFix(face->underline_thickness, face->size->metrics.y_scale));
729 underline_position = QFixed::fromFixed(-FT_MulFix(face->underline_position, face->size->metrics.y_scale));
731 // copied from QFontEngineQPF
733 int score = fontDef.weight * fontDef.pixelSize;
734 line_thickness = score / 700;
735 // looks better with thicker line for small pointsizes
736 if (line_thickness < 2 && score >= 1050)
738 underline_position = ((line_thickness * 2) + 3) / 6;
740 if (line_thickness < 1)
743 hbFont.x_ppem = face->size->metrics.x_ppem;
744 hbFont.y_ppem = face->size->metrics.y_ppem;
745 hbFont.x_scale = face->size->metrics.x_scale;
746 hbFont.y_scale = face->size->metrics.y_scale;
748 hbFace = freetype->hbFace;
750 metrics = face->size->metrics;
753 TrueType fonts with embedded bitmaps may have a bitmap font specific
754 ascent/descent in the EBLC table. There is no direct public API
755 to extract those values. The only way we've found is to trick freetype
756 into thinking that it's not a scalable font in FT_SelectSize so that
757 the metrics are retrieved from the bitmap strikes.
759 if (FT_IS_SCALABLE(face)) {
760 for (int i = 0; i < face->num_fixed_sizes; ++i) {
761 if (xsize == X_SIZE(face, i) && ysize == Y_SIZE(face, i)) {
762 face->face_flags &= ~FT_FACE_FLAG_SCALABLE;
764 FT_Select_Size(face, i);
765 metrics.ascender = face->size->metrics.ascender;
766 metrics.descender = face->size->metrics.descender;
767 FT_Set_Char_Size(face, xsize, ysize, 0, 0);
769 face->face_flags |= FT_FACE_FLAG_SCALABLE;
775 fontDef.styleName = QString::fromUtf8(face->style_name);
779 fsType = freetype->fsType();
783 void QFontEngineFT::setDefaultHintStyle(HintStyle style)
785 default_hint_style = style;
788 int QFontEngineFT::loadFlags(QGlyphSet *set, GlyphFormat format, int flags,
789 bool &hsubpixel, int &vfactor) const
791 int load_flags = FT_LOAD_DEFAULT | default_load_flags;
792 int load_target = default_hint_style == HintLight
793 ? FT_LOAD_TARGET_LIGHT
794 : FT_LOAD_TARGET_NORMAL;
796 if (format == Format_Mono) {
797 load_target = FT_LOAD_TARGET_MONO;
798 } else if (format == Format_A32) {
799 if (subpixelType == QFontEngineFT::Subpixel_RGB || subpixelType == QFontEngineFT::Subpixel_BGR) {
800 if (default_hint_style == HintFull)
801 load_target = FT_LOAD_TARGET_LCD;
803 } else if (subpixelType == QFontEngineFT::Subpixel_VRGB || subpixelType == QFontEngineFT::Subpixel_VBGR) {
804 if (default_hint_style == HintFull)
805 load_target = FT_LOAD_TARGET_LCD_V;
810 if (set && set->outline_drawing)
811 load_flags = FT_LOAD_NO_BITMAP;
813 if (default_hint_style == HintNone || (flags & HB_ShaperFlag_UseDesignMetrics) || (set && set->outline_drawing))
814 load_flags |= FT_LOAD_NO_HINTING;
816 load_flags |= load_target;
821 QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph,
822 QFixed subPixelPosition,
824 bool fetchMetricsOnly) const
826 // Q_ASSERT(freetype->lock == 1);
828 if (format == Format_None) {
829 if (defaultFormat != Format_None) {
830 format = defaultFormat;
832 format = Format_Mono;
836 Glyph *g = set ? set->getGlyph(glyph, subPixelPosition) : 0;
837 if (g && g->format == format)
840 QFontEngineFT::GlyphInfo info;
842 Q_ASSERT(format != Format_None);
843 bool hsubpixel = false;
845 int load_flags = loadFlags(set, format, 0, hsubpixel, vfactor);
847 if (format != Format_Mono && !embeddedbitmap)
848 load_flags |= FT_LOAD_NO_BITMAP;
850 FT_Matrix matrix = freetype->matrix;
851 bool transform = matrix.xx != 0x10000
852 || matrix.yy != 0x10000
857 load_flags |= FT_LOAD_NO_BITMAP;
859 FT_Face face = freetype->face;
862 v.x = format == Format_Mono ? 0 : FT_Pos(subPixelPosition.toReal() * 64);
864 FT_Set_Transform(face, &freetype->matrix, &v);
866 FT_Error err = FT_Load_Glyph(face, glyph, load_flags);
867 if (err && (load_flags & FT_LOAD_NO_BITMAP)) {
868 load_flags &= ~FT_LOAD_NO_BITMAP;
869 err = FT_Load_Glyph(face, glyph, load_flags);
871 if (err == FT_Err_Too_Few_Arguments) {
872 // this is an error in the bytecode interpreter, just try to run without it
873 load_flags |= FT_LOAD_FORCE_AUTOHINT;
874 err = FT_Load_Glyph(face, glyph, load_flags);
876 if (err != FT_Err_Ok)
877 qWarning("load glyph failed err=%x face=%p, glyph=%d", err, face, glyph);
879 if ((!set || set->outline_drawing) && fetchMetricsOnly)
882 FT_GlyphSlot slot = face->glyph;
883 if (embolden) Q_FT_GLYPHSLOT_EMBOLDEN(slot);
885 Q_FT_GLYPHSLOT_OBLIQUE(slot);
887 // While Embolden alters the metrics of the slot, oblique does not, so we need
888 // to fix this ourselves.
896 FT_Matrix_Multiply(&m, &matrix);
899 FT_Library library = qt_getFreetype();
901 info.xOff = TRUNC(ROUND(slot->advance.x));
904 uchar *glyph_buffer = 0;
905 int glyph_buffer_size = 0;
906 #if defined(QT_USE_FREETYPE_LCDFILTER)
907 bool useFreetypeRenderGlyph = false;
908 if (slot->format == FT_GLYPH_FORMAT_OUTLINE && (hsubpixel || vfactor != 1)) {
909 err = FT_Library_SetLcdFilter(library, (FT_LcdFilter)lcdFilterType);
910 if (err == FT_Err_Ok)
911 useFreetypeRenderGlyph = true;
914 if (useFreetypeRenderGlyph) {
915 err = FT_Render_Glyph(slot, hsubpixel ? FT_RENDER_MODE_LCD : FT_RENDER_MODE_LCD_V);
917 if (err != FT_Err_Ok)
918 qWarning("render glyph failed err=%x face=%p, glyph=%d", err, face, glyph);
920 FT_Library_SetLcdFilter(library, FT_LCD_FILTER_NONE);
922 info.height = slot->bitmap.rows / vfactor;
923 info.width = hsubpixel ? slot->bitmap.width / 3 : slot->bitmap.width;
924 info.x = -slot->bitmap_left;
925 info.y = slot->bitmap_top;
927 glyph_buffer_size = info.width * info.height * 4;
928 glyph_buffer = new uchar[glyph_buffer_size];
931 convertRGBToARGB(slot->bitmap.buffer, (uint *)glyph_buffer, info.width, info.height, slot->bitmap.pitch, subpixelType != QFontEngineFT::Subpixel_RGB, false);
932 else if (vfactor != 1)
933 convertRGBToARGB_V(slot->bitmap.buffer, (uint *)glyph_buffer, info.width, info.height, slot->bitmap.pitch, subpixelType != QFontEngineFT::Subpixel_VRGB, false);
937 int left = slot->metrics.horiBearingX;
938 int right = slot->metrics.horiBearingX + slot->metrics.width;
939 int top = slot->metrics.horiBearingY;
940 int bottom = slot->metrics.horiBearingY - slot->metrics.height;
941 if(transform && slot->format != FT_GLYPH_FORMAT_BITMAP) {
946 FT_Vector_Transform(&vector, &matrix);
951 FT_Vector_Transform(&vector, &matrix);
952 if (l > vector.x) l = vector.x;
953 if (r < vector.x) r = vector.x;
954 if (t < vector.y) t = vector.y;
955 if (b > vector.y) b = vector.y;
958 FT_Vector_Transform(&vector, &matrix);
959 if (l > vector.x) l = vector.x;
960 if (r < vector.x) r = vector.x;
961 if (t < vector.y) t = vector.y;
962 if (b > vector.y) b = vector.y;
965 FT_Vector_Transform(&vector, &matrix);
966 if (l > vector.x) l = vector.x;
967 if (r < vector.x) r = vector.x;
968 if (t < vector.y) t = vector.y;
969 if (b > vector.y) b = vector.y;
977 bottom = FLOOR(bottom);
980 int hpixels = TRUNC(right - left);
981 // subpixel position requires one more pixel
982 if (subPixelPosition > 0 && format != Format_Mono)
986 hpixels = hpixels*3 + 8;
987 info.width = hpixels;
988 info.height = TRUNC(top - bottom);
989 info.x = -TRUNC(left);
996 bool large_glyph = (((short)(slot->linearHoriAdvance>>10) != slot->linearHoriAdvance>>10)
997 || ((uchar)(info.width) != info.width)
998 || ((uchar)(info.height) != info.height)
999 || ((signed char)(info.x) != info.x)
1000 || ((signed char)(info.y) != info.y)
1001 || ((signed char)(info.xOff) != info.xOff));
1004 delete [] glyph_buffer;
1008 int pitch = (format == Format_Mono ? ((info.width + 31) & ~31) >> 3 :
1009 (format == Format_A8 ? (info.width + 3) & ~3 : info.width * 4));
1010 glyph_buffer_size = pitch * info.height;
1011 glyph_buffer = new uchar[glyph_buffer_size];
1013 if (slot->format == FT_GLYPH_FORMAT_OUTLINE) {
1015 bitmap.rows = info.height*vfactor;
1016 bitmap.width = hpixels;
1017 bitmap.pitch = format == Format_Mono ? (((info.width + 31) & ~31) >> 3) : ((bitmap.width + 3) & ~3);
1018 if (!hsubpixel && vfactor == 1)
1019 bitmap.buffer = glyph_buffer;
1021 bitmap.buffer = new uchar[bitmap.rows*bitmap.pitch];
1022 memset(bitmap.buffer, 0, bitmap.rows*bitmap.pitch);
1023 bitmap.pixel_mode = format == Format_Mono ? FT_PIXEL_MODE_MONO : FT_PIXEL_MODE_GRAY;
1025 matrix.xx = (hsubpixel ? 3 : 1) << 16;
1026 matrix.yy = vfactor << 16;
1027 matrix.yx = matrix.xy = 0;
1029 FT_Outline_Transform(&slot->outline, &matrix);
1030 FT_Outline_Translate (&slot->outline, (hsubpixel ? -3*left +(4<<6) : -left), -bottom*vfactor);
1031 FT_Outline_Get_Bitmap(library, &slot->outline, &bitmap);
1033 Q_ASSERT (bitmap.pixel_mode == FT_PIXEL_MODE_GRAY);
1034 Q_ASSERT(antialias);
1035 uchar *convoluted = new uchar[bitmap.rows*bitmap.pitch];
1036 bool useLegacyLcdFilter = false;
1037 #if defined(FC_LCD_FILTER) && defined(FT_LCD_FILTER_H)
1038 useLegacyLcdFilter = (lcdFilterType == FT_LCD_FILTER_LEGACY);
1040 uchar *buffer = bitmap.buffer;
1041 if (!useLegacyLcdFilter) {
1042 convoluteBitmap(bitmap.buffer, convoluted, bitmap.width, info.height, bitmap.pitch);
1043 buffer = convoluted;
1045 convertRGBToARGB(buffer + 1, (uint *)glyph_buffer, info.width, info.height, bitmap.pitch, subpixelType != QFontEngineFT::Subpixel_RGB, useLegacyLcdFilter);
1046 delete [] convoluted;
1047 } else if (vfactor != 1) {
1048 convertRGBToARGB_V(bitmap.buffer, (uint *)glyph_buffer, info.width, info.height, bitmap.pitch, subpixelType != QFontEngineFT::Subpixel_VRGB, true);
1051 if (bitmap.buffer != glyph_buffer)
1052 delete [] bitmap.buffer;
1053 } else if (slot->format == FT_GLYPH_FORMAT_BITMAP) {
1054 Q_ASSERT(slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO);
1055 uchar *src = slot->bitmap.buffer;
1056 uchar *dst = glyph_buffer;
1057 int h = slot->bitmap.rows;
1058 if (format == Format_Mono) {
1059 int bytes = ((info.width + 7) & ~7) >> 3;
1061 memcpy (dst, src, bytes);
1063 src += slot->bitmap.pitch;
1068 uint *dd = (uint *)dst;
1070 for (int x = 0; x < slot->bitmap.width; x++) {
1071 uint a = ((src[x >> 3] & (0x80 >> (x & 7))) ? 0xffffff : 0x000000);
1076 src += slot->bitmap.pitch;
1078 } else if (vfactor != 1) {
1080 uint *dd = (uint *)dst;
1081 for (int x = 0; x < slot->bitmap.width; x++) {
1082 uint a = ((src[x >> 3] & (0x80 >> (x & 7))) ? 0xffffff : 0x000000);
1086 src += slot->bitmap.pitch;
1090 for (int x = 0; x < slot->bitmap.width; x++) {
1091 unsigned char a = ((src[x >> 3] & (0x80 >> (x & 7))) ? 0xff : 0x00);
1095 src += slot->bitmap.pitch;
1100 qWarning("QFontEngine: Glyph neither outline nor bitmap format=%d", slot->format);
1101 delete [] glyph_buffer;
1112 g->linearAdvance = slot->linearHoriAdvance >> 10;
1113 g->width = info.width;
1114 g->height = info.height;
1117 g->advance = info.xOff;
1120 g->data = glyph_buffer;
1123 set->setGlyph(glyph, subPixelPosition, g);
1128 QFontEngine::FaceId QFontEngineFT::faceId() const
1133 QFontEngine::Properties QFontEngineFT::properties() const
1135 Properties p = freetype->properties();
1136 if (p.postscriptName.isEmpty()) {
1137 p.postscriptName = QFontEngine::convertToPostscriptFontFamilyName(fontDef.family.toUtf8());
1140 return freetype->properties();
1143 QFixed QFontEngineFT::emSquareSize() const
1145 if (FT_IS_SCALABLE(freetype->face))
1146 return freetype->face->units_per_EM;
1148 return freetype->face->size->metrics.y_ppem;
1151 bool QFontEngineFT::getSfntTableData(uint tag, uchar *buffer, uint *length) const
1153 return freetype->getSfntTable(tag, buffer, length);
1156 int QFontEngineFT::synthesized() const
1159 if ((fontDef.style != QFont::StyleNormal) && !(freetype->face->style_flags & FT_STYLE_FLAG_ITALIC))
1160 s = SynthesizedItalic;
1161 if ((fontDef.weight == QFont::Bold) && !(freetype->face->style_flags & FT_STYLE_FLAG_BOLD))
1162 s |= SynthesizedBold;
1163 if (fontDef.stretch != 100 && FT_IS_SCALABLE(freetype->face))
1164 s |= SynthesizedStretch;
1168 QFixed QFontEngineFT::ascent() const
1170 return QFixed::fromFixed(metrics.ascender);
1173 QFixed QFontEngineFT::descent() const
1175 // subtract a pixel to work around QFontMetrics's built-in + 1
1176 return QFixed::fromFixed(-metrics.descender - 64);
1179 QFixed QFontEngineFT::leading() const
1181 return QFixed::fromFixed(metrics.height - metrics.ascender + metrics.descender);
1184 QFixed QFontEngineFT::xHeight() const
1186 TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2);
1187 if (os2 && os2->sxHeight) {
1189 QFixed answer = QFixed(os2->sxHeight*freetype->face->size->metrics.y_ppem)/freetype->face->units_per_EM;
1193 return QFontEngine::xHeight();
1196 QFixed QFontEngineFT::averageCharWidth() const
1198 TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2);
1199 if (os2 && os2->xAvgCharWidth) {
1201 QFixed answer = QFixed(os2->xAvgCharWidth*freetype->face->size->metrics.x_ppem)/freetype->face->units_per_EM;
1205 return QFontEngine::averageCharWidth();
1208 qreal QFontEngineFT::maxCharWidth() const
1210 return metrics.max_advance >> 6;
1213 static const ushort char_table[] = {
1235 static const int char_table_entries = sizeof(char_table)/sizeof(ushort);
1238 qreal QFontEngineFT::minLeftBearing() const
1240 if (lbearing == SHRT_MIN)
1241 (void) minRightBearing(); // calculates both
1242 return lbearing.toReal();
1245 qreal QFontEngineFT::minRightBearing() const
1247 if (rbearing == SHRT_MIN) {
1248 lbearing = rbearing = 0;
1249 const QChar *ch = (const QChar *)(const void*)char_table;
1250 QGlyphLayoutArray<char_table_entries> glyphs;
1251 int ng = char_table_entries;
1252 stringToCMap(ch, char_table_entries, &glyphs, &ng, QTextEngine::GlyphIndicesOnly);
1254 if (glyphs.glyphs[ng]) {
1255 glyph_metrics_t gi = const_cast<QFontEngineFT *>(this)->boundingBox(glyphs.glyphs[ng]);
1256 lbearing = qMin(lbearing, gi.x);
1257 rbearing = qMin(rbearing, (gi.xoff - gi.x - gi.width));
1261 return rbearing.toReal();
1264 QFixed QFontEngineFT::lineThickness() const
1266 return line_thickness;
1269 QFixed QFontEngineFT::underlinePosition() const
1271 return underline_position;
1274 void QFontEngineFT::doKerning(QGlyphLayout *g, QTextEngine::ShaperFlags flags) const
1276 if (!kerning_pairs_loaded) {
1277 kerning_pairs_loaded = true;
1279 if (freetype->face->size->metrics.x_ppem != 0) {
1280 QFixed scalingFactor(freetype->face->units_per_EM/freetype->face->size->metrics.x_ppem);
1282 const_cast<QFontEngineFT *>(this)->loadKerningPairs(scalingFactor);
1287 QFontEngine::doKerning(g, flags);
1290 QFontEngineFT::QGlyphSet *QFontEngineFT::loadTransformedGlyphSet(const QTransform &matrix)
1292 if (matrix.type() > QTransform::TxShear)
1295 // FT_Set_Transform only supports scalable fonts
1296 if (!FT_IS_SCALABLE(freetype->face))
1300 m.xx = FT_Fixed(matrix.m11() * 65536);
1301 m.xy = FT_Fixed(-matrix.m21() * 65536);
1302 m.yx = FT_Fixed(-matrix.m12() * 65536);
1303 m.yy = FT_Fixed(matrix.m22() * 65536);
1307 for (int i = 0; i < transformedGlyphSets.count(); ++i) {
1308 const QGlyphSet &g = transformedGlyphSets.at(i);
1309 if (g.transformationMatrix.xx == m.xx
1310 && g.transformationMatrix.xy == m.xy
1311 && g.transformationMatrix.yx == m.yx
1312 && g.transformationMatrix.yy == m.yy) {
1314 // found a match, move it to the front
1315 transformedGlyphSets.move(i, 0);
1316 gs = &transformedGlyphSets[0];
1322 // don't try to load huge fonts
1323 bool draw_as_outline = fontDef.pixelSize * qSqrt(qAbs(matrix.det())) >= QT_MAX_CACHED_GLYPH_SIZE;
1324 if (draw_as_outline)
1327 // don't cache more than 10 transformations
1328 if (transformedGlyphSets.count() >= 10) {
1329 transformedGlyphSets.move(transformedGlyphSets.size() - 1, 0);
1331 transformedGlyphSets.prepend(QGlyphSet());
1333 gs = &transformedGlyphSets[0];
1335 gs->transformationMatrix = m;
1336 gs->outline_drawing = draw_as_outline;
1342 bool QFontEngineFT::loadGlyphs(QGlyphSet *gs, const glyph_t *glyphs, int num_glyphs,
1343 const QFixedPoint *positions,
1348 for (int i = 0; i < num_glyphs; ++i) {
1349 QFixed spp = subPixelPositionForX(positions[i].x);
1350 Glyph *glyph = gs ? gs->getGlyph(glyphs[i], spp) : 0;
1351 if (glyph == 0 || glyph->format != format) {
1354 FT_Matrix m = matrix;
1355 FT_Matrix_Multiply(&gs->transformationMatrix, &m);
1356 FT_Set_Transform(face, &m, 0);
1357 freetype->matrix = m;
1359 if (!loadGlyph(gs, glyphs[i], spp, format)) {
1372 void QFontEngineFT::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics)
1374 FT_Face face = lockFace(Unscaled);
1375 FT_Set_Transform(face, 0, 0);
1376 FT_Load_Glyph(face, glyph, FT_LOAD_NO_BITMAP);
1378 int left = face->glyph->metrics.horiBearingX;
1379 int right = face->glyph->metrics.horiBearingX + face->glyph->metrics.width;
1380 int top = face->glyph->metrics.horiBearingY;
1381 int bottom = face->glyph->metrics.horiBearingY - face->glyph->metrics.height;
1387 metrics->width = QFixed::fromFixed(right-left);
1388 metrics->height = QFixed::fromFixed(top-bottom);
1389 metrics->x = QFixed::fromFixed(left);
1390 metrics->y = QFixed::fromFixed(-top);
1391 metrics->xoff = QFixed::fromFixed(face->glyph->advance.x);
1393 if (!FT_IS_SCALABLE(freetype->face))
1394 QFreetypeFace::addBitmapToPath(face->glyph, p, path);
1396 QFreetypeFace::addGlyphToPath(face, face->glyph, p, path, face->units_per_EM << 6, face->units_per_EM << 6);
1398 FT_Set_Transform(face, &freetype->matrix, 0);
1402 static inline unsigned int getChar(const QChar *str, int &i, const int len)
1404 uint ucs4 = str[i].unicode();
1405 if (str[i].isHighSurrogate() && i < len-1 && str[i+1].isLowSurrogate()) {
1407 ucs4 = QChar::surrogateToUcs4(ucs4, str[i].unicode());
1412 bool QFontEngineFT::canRender(const QChar *string, int len)
1414 FT_Face face = freetype->face;
1418 for ( int i = 0; i < len; i++ ) {
1419 unsigned int uc = getChar(string, i, len);
1420 if (!FcCharSetHasChar (_font->charset, uc) && getAdobeCharIndex(face, _cmap, uc) == 0) {
1429 for ( int i = 0; i < len; i++ ) {
1430 unsigned int uc = getChar(string, i, len);
1431 if (!FT_Get_Char_Index(face, uc))
1438 void QFontEngineFT::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags)
1440 if (!glyphs.numGlyphs)
1443 if (FT_IS_SCALABLE(freetype->face)) {
1444 QFontEngine::addOutlineToPath(x, y, glyphs, path, flags);
1446 QVarLengthArray<QFixedPoint> positions;
1447 QVarLengthArray<glyph_t> positioned_glyphs;
1449 matrix.translate(x, y);
1450 getGlyphPositions(glyphs, matrix, flags, positioned_glyphs, positions);
1452 FT_Face face = lockFace(Unscaled);
1453 for (int gl = 0; gl < glyphs.numGlyphs; gl++) {
1454 FT_UInt glyph = positioned_glyphs[gl];
1455 FT_Load_Glyph(face, glyph, FT_LOAD_TARGET_MONO);
1456 freetype->addBitmapToPath(face->glyph, positions[gl], path);
1462 void QFontEngineFT::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int numGlyphs,
1463 QPainterPath *path, QTextItem::RenderFlags)
1465 FT_Face face = lockFace(Unscaled);
1467 for (int gl = 0; gl < numGlyphs; gl++) {
1468 FT_UInt glyph = glyphs[gl];
1470 FT_Load_Glyph(face, glyph, FT_LOAD_NO_BITMAP);
1472 FT_GlyphSlot g = face->glyph;
1473 if (g->format != FT_GLYPH_FORMAT_OUTLINE)
1475 if (embolden) Q_FT_GLYPHSLOT_EMBOLDEN(g);
1476 if (obliquen) Q_FT_GLYPHSLOT_OBLIQUE(g);
1477 QFreetypeFace::addGlyphToPath(face, g, positions[gl], path, xsize, ysize);
1482 bool QFontEngineFT::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs,
1483 QTextEngine::ShaperFlags flags) const
1485 if (*nglyphs < len) {
1490 bool mirrored = flags & QTextEngine::RightToLeft;
1492 if (freetype->symbol_map) {
1493 FT_Face face = freetype->face;
1494 for ( int i = 0; i < len; ++i ) {
1495 unsigned int uc = getChar(str, i, len);
1496 glyphs->glyphs[glyph_pos] = uc < QFreetypeFace::cmapCacheSize ? freetype->cmapCache[uc] : 0;
1497 if ( !glyphs->glyphs[glyph_pos] ) {
1499 #if !defined(QT_NO_FONTCONFIG)
1500 if (freetype->charset != 0 && FcCharSetHasChar(freetype->charset, uc)) {
1505 glyph = FT_Get_Char_Index(face, uc);
1506 if (!glyph && (uc == 0xa0 || uc == 0x9)) {
1511 FT_Set_Charmap(face, freetype->symbol_map);
1512 glyph = FT_Get_Char_Index(face, uc);
1513 FT_Set_Charmap(face, freetype->unicode_map);
1515 glyphs->glyphs[glyph_pos] = glyph;
1516 if (uc < QFreetypeFace::cmapCacheSize)
1517 freetype->cmapCache[uc] = glyph;
1522 FT_Face face = freetype->face;
1523 for (int i = 0; i < len; ++i) {
1524 unsigned int uc = getChar(str, i, len);
1526 uc = QChar::mirroredChar(uc);
1527 glyphs->glyphs[glyph_pos] = uc < QFreetypeFace::cmapCacheSize ? freetype->cmapCache[uc] : 0;
1528 if (!glyphs->glyphs[glyph_pos]) {
1529 #if !defined(QT_NO_FONTCONFIG)
1530 if (freetype->charset == 0 || FcCharSetHasChar(freetype->charset, uc))
1534 glyph_t glyph = FT_Get_Char_Index(face, uc);
1535 if (!glyph && (uc == 0xa0 || uc == 0x9)) {
1539 glyphs->glyphs[glyph_pos] = glyph;
1540 if (uc < QFreetypeFace::cmapCacheSize)
1541 freetype->cmapCache[uc] = glyph;
1548 *nglyphs = glyph_pos;
1549 glyphs->numGlyphs = glyph_pos;
1551 if (flags & QTextEngine::GlyphIndicesOnly)
1554 recalcAdvances(glyphs, flags);
1559 void QFontEngineFT::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const
1562 bool design = (default_hint_style == HintNone ||
1563 default_hint_style == HintLight ||
1564 (flags & HB_ShaperFlag_UseDesignMetrics)) && FT_IS_SCALABLE(freetype->face);
1565 for (int i = 0; i < glyphs->numGlyphs; i++) {
1566 Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(glyphs->glyphs[i]) : 0;
1568 glyphs->advances_x[i] = design ? QFixed::fromFixed(g->linearAdvance) : QFixed(g->advance);
1572 g = loadGlyph(cacheEnabled ? &defaultGlyphSet : 0, glyphs->glyphs[i], 0, Format_None, true);
1573 glyphs->advances_x[i] = design ? QFixed::fromFixed(face->glyph->linearHoriAdvance >> 10)
1574 : QFixed::fromFixed(face->glyph->metrics.horiAdvance).round();
1576 if (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
1577 glyphs->advances_x[i] = glyphs->advances_x[i].round();
1578 glyphs->advances_y[i] = 0;
1584 glyph_metrics_t QFontEngineFT::boundingBox(const QGlyphLayout &glyphs)
1588 glyph_metrics_t overall;
1589 // initialize with line height, we get the same behaviour on all platforms
1590 overall.y = -ascent();
1591 overall.height = ascent() + descent() + 1;
1595 for (int i = 0; i < glyphs.numGlyphs; i++) {
1596 Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(glyphs.glyphs[i]) : 0;
1600 g = loadGlyph(cacheEnabled ? &defaultGlyphSet : 0, glyphs.glyphs[i], 0, Format_None, true);
1603 QFixed x = overall.xoff + glyphs.offsets[i].x + g->x;
1604 QFixed y = overall.yoff + glyphs.offsets[i].y - g->y;
1605 overall.x = qMin(overall.x, x);
1606 overall.y = qMin(overall.y, y);
1607 xmax = qMax(xmax, x + g->width);
1608 ymax = qMax(ymax, y + g->height);
1609 overall.xoff += g->advance;
1611 int left = FLOOR(face->glyph->metrics.horiBearingX);
1612 int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width);
1613 int top = CEIL(face->glyph->metrics.horiBearingY);
1614 int bottom = FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height);
1616 QFixed x = overall.xoff + glyphs.offsets[i].x - (-TRUNC(left));
1617 QFixed y = overall.yoff + glyphs.offsets[i].y - TRUNC(top);
1618 overall.x = qMin(overall.x, x);
1619 overall.y = qMin(overall.y, y);
1620 xmax = qMax(xmax, x + TRUNC(right - left));
1621 ymax = qMax(ymax, y + TRUNC(top - bottom));
1622 overall.xoff += int(TRUNC(ROUND(face->glyph->advance.x)));
1625 overall.height = qMax(overall.height, ymax - overall.y);
1626 overall.width = xmax - overall.x;
1634 glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph)
1637 glyph_metrics_t overall;
1638 Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(glyph) : 0;
1641 g = loadGlyph(cacheEnabled ? &defaultGlyphSet : 0, glyph, 0, Format_None, true);
1646 overall.width = g->width;
1647 overall.height = g->height;
1648 overall.xoff = g->advance;
1649 if (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
1650 overall.xoff = overall.xoff.round();
1652 int left = FLOOR(face->glyph->metrics.horiBearingX);
1653 int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width);
1654 int top = CEIL(face->glyph->metrics.horiBearingY);
1655 int bottom = FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height);
1657 overall.width = TRUNC(right-left);
1658 overall.height = TRUNC(top-bottom);
1659 overall.x = TRUNC(left);
1660 overall.y = -TRUNC(top);
1661 overall.xoff = TRUNC(ROUND(face->glyph->advance.x));
1668 glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph, const QTransform &matrix)
1670 return alphaMapBoundingBox(glyph, 0, matrix, QFontEngine::Format_None);
1673 static FT_Matrix QTransformToFTMatrix(const QTransform &matrix)
1677 m.xx = FT_Fixed(matrix.m11() * 65536);
1678 m.xy = FT_Fixed(-matrix.m21() * 65536);
1679 m.yx = FT_Fixed(-matrix.m12() * 65536);
1680 m.yy = FT_Fixed(matrix.m22() * 65536);
1685 glyph_metrics_t QFontEngineFT::alphaMapBoundingBox(glyph_t glyph, QFixed subPixelPosition, const QTransform &matrix, QFontEngine::GlyphFormat format)
1688 glyph_metrics_t overall;
1689 QGlyphSet *glyphSet = 0;
1690 FT_Matrix ftMatrix = QTransformToFTMatrix(matrix);
1692 if (matrix.type() > QTransform::TxTranslate && FT_IS_SCALABLE(freetype->face)) {
1693 // TODO move everything here to a method of its own to access glyphSets
1694 // to be shared with a new method that will replace loadTransformedGlyphSet()
1695 for (int i = 0; i < transformedGlyphSets.count(); ++i) {
1696 const QGlyphSet &g = transformedGlyphSets.at(i);
1697 if (g.transformationMatrix.xx == ftMatrix.xx
1698 && g.transformationMatrix.xy == ftMatrix.xy
1699 && g.transformationMatrix.yx == ftMatrix.yx
1700 && g.transformationMatrix.yy == ftMatrix.yy) {
1702 // found a match, move it to the front
1703 transformedGlyphSets.move(i, 0);
1704 glyphSet = &transformedGlyphSets[0];
1710 // don't cache more than 10 transformations
1711 if (transformedGlyphSets.count() >= 10) {
1712 transformedGlyphSets.move(transformedGlyphSets.size() - 1, 0);
1714 transformedGlyphSets.prepend(QGlyphSet());
1716 glyphSet = &transformedGlyphSets[0];
1718 glyphSet->transformationMatrix = ftMatrix;
1722 glyphSet = &defaultGlyphSet;
1725 Glyph * g = glyphSet ? glyphSet->getGlyph(glyph) : 0;
1726 if (!g || g->format != format) {
1728 FT_Matrix m = this->matrix;
1729 FT_Matrix_Multiply(&ftMatrix, &m);
1730 freetype->matrix = m;
1731 g = loadGlyph(glyphSet, glyph, subPixelPosition, format, false);
1737 overall.width = g->width;
1738 overall.height = g->height;
1739 overall.xoff = g->advance;
1741 int left = FLOOR(face->glyph->metrics.horiBearingX);
1742 int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width);
1743 int top = CEIL(face->glyph->metrics.horiBearingY);
1744 int bottom = FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height);
1746 overall.width = TRUNC(right-left);
1747 overall.height = TRUNC(top-bottom);
1748 overall.x = TRUNC(left);
1749 overall.y = -TRUNC(top);
1750 overall.xoff = TRUNC(ROUND(face->glyph->advance.x));
1757 QImage *QFontEngineFT::lockedAlphaMapForGlyph(glyph_t glyphIndex, QFixed subPixelPosition,
1758 QFontEngine::GlyphFormat neededFormat,
1759 const QTransform &t, QPoint *offset)
1761 Q_ASSERT(currentlyLockedAlphaMap.isNull());
1765 neededFormat = Format_Mono;
1766 else if (neededFormat == Format_None && defaultFormat != Format_None)
1767 neededFormat = defaultFormat;
1768 else if (neededFormat == Format_None)
1769 neededFormat = Format_A8;
1771 QImage::Format format;
1772 switch (neededFormat) {
1774 format = QImage::Format_Mono;
1777 format = QImage::Format_Indexed8;
1780 format = QImage::Format_ARGB32;
1784 format = QImage::Format_Invalid;
1787 QFontEngineFT::Glyph *glyph;
1789 QFontEngineFT::QGlyphSet *gset = &defaultGlyphSet;
1790 if (t.type() >= QTransform::TxScale) {
1792 gset = loadTransformedGlyphSet(t);
1798 FT_Matrix m = matrix;
1799 FT_Matrix_Multiply(&gset->transformationMatrix, &m);
1800 FT_Set_Transform(freetype->face, &m, 0);
1801 freetype->matrix = m;
1804 if (!gset || gset->outline_drawing || !loadGlyph(gset, glyphIndex, subPixelPosition,
1806 return QFontEngine::lockedAlphaMapForGlyph(glyphIndex, subPixelPosition, neededFormat, t,
1810 glyph = gset->getGlyph(glyphIndex, subPixelPosition);
1812 FT_Matrix m = matrix;
1813 FT_Matrix extra = QTransformToFTMatrix(t);
1814 FT_Matrix_Multiply(&extra, &m);
1815 FT_Set_Transform(freetype->face, &m, 0);
1816 freetype->matrix = m;
1817 glyph = loadGlyph(0, glyphIndex, subPixelPosition, neededFormat);
1820 if (glyph == 0 || glyph->data == 0 || glyph->width == 0 || glyph->height == 0) {
1826 switch (neededFormat) {
1828 pitch = ((glyph->width + 31) & ~31) >> 3;
1831 pitch = (glyph->width + 3) & ~3;
1834 pitch = glyph->width * 4;
1842 *offset = QPoint(glyph->x, -glyph->y);
1844 currentlyLockedAlphaMap = QImage(glyph->data, glyph->width, glyph->height, pitch, format);
1845 Q_ASSERT(!currentlyLockedAlphaMap.isNull());
1847 QImageData *data = currentlyLockedAlphaMap.data_ptr();
1848 data->is_locked = true;
1850 return ¤tlyLockedAlphaMap;
1853 void QFontEngineFT::unlockAlphaMapForGlyph()
1855 Q_ASSERT(!currentlyLockedAlphaMap.isNull());
1857 currentlyLockedAlphaMap = QImage();
1860 QFontEngineFT::Glyph *QFontEngineFT::loadGlyphFor(glyph_t g, QFixed subPixelPosition, GlyphFormat format)
1862 return defaultGlyphSet.outline_drawing ? 0 :
1863 loadGlyph(cacheEnabled ? &defaultGlyphSet : 0, g, subPixelPosition, format);
1866 QImage QFontEngineFT::alphaMapForGlyph(glyph_t g, QFixed subPixelPosition)
1870 Glyph *glyph = loadGlyphFor(g, subPixelPosition, antialias ? Format_A8 : Format_Mono);
1873 return QFontEngine::alphaMapForGlyph(g);
1876 const int pitch = antialias ? (glyph->width + 3) & ~3 : ((glyph->width + 31)/32) * 4;
1878 QImage img(glyph->width, glyph->height, antialias ? QImage::Format_Indexed8 : QImage::Format_Mono);
1880 QVector<QRgb> colors(256);
1881 for (int i=0; i<256; ++i)
1882 colors[i] = qRgba(0, 0, 0, i);
1883 img.setColorTable(colors);
1885 QVector<QRgb> colors(2);
1886 colors[0] = qRgba(0, 0, 0, 0);
1887 colors[1] = qRgba(0, 0, 0, 255);
1888 img.setColorTable(colors);
1890 Q_ASSERT(img.bytesPerLine() == pitch);
1892 for (int y = 0; y < glyph->height; ++y)
1893 memcpy(img.scanLine(y), &glyph->data[y * pitch], pitch);
1900 QImage QFontEngineFT::alphaRGBMapForGlyph(glyph_t g, QFixed subPixelPosition, const QTransform &t)
1902 if (t.type() > QTransform::TxTranslate)
1903 return QFontEngine::alphaRGBMapForGlyph(g, subPixelPosition, t);
1907 Glyph *glyph = loadGlyphFor(g, subPixelPosition, Format_A32);
1910 return QFontEngine::alphaRGBMapForGlyph(g, subPixelPosition, t);
1913 QImage img(glyph->width, glyph->height, QImage::Format_RGB32);
1914 memcpy(img.bits(), glyph->data, 4 * glyph->width * glyph->height);
1920 void QFontEngineFT::removeGlyphFromCache(glyph_t glyph)
1922 defaultGlyphSet.removeGlyphFromCache(glyph, 0);
1925 int QFontEngineFT::glyphCount() const
1928 FT_Face face = lockFace();
1930 count = face->num_glyphs;
1936 FT_Face QFontEngineFT::lockFace(Scaling scale) const
1939 FT_Face face = freetype->face;
1940 if (scale == Unscaled) {
1941 FT_Set_Char_Size(face, face->units_per_EM << 6, face->units_per_EM << 6, 0, 0);
1942 freetype->xsize = face->units_per_EM << 6;
1943 freetype->ysize = face->units_per_EM << 6;
1944 } else if (freetype->xsize != xsize || freetype->ysize != ysize) {
1945 FT_Set_Char_Size(face, xsize, ysize, 0, 0);
1946 freetype->xsize = xsize;
1947 freetype->ysize = ysize;
1949 if (freetype->matrix.xx != matrix.xx ||
1950 freetype->matrix.yy != matrix.yy ||
1951 freetype->matrix.xy != matrix.xy ||
1952 freetype->matrix.yx != matrix.yx) {
1953 freetype->matrix = matrix;
1954 FT_Set_Transform(face, &freetype->matrix, 0);
1960 void QFontEngineFT::unlockFace() const
1965 FT_Face QFontEngineFT::non_locked_face() const
1967 return freetype->face;
1971 QFontEngineFT::QGlyphSet::QGlyphSet()
1972 : outline_drawing(false)
1974 transformationMatrix.xx = 0x10000;
1975 transformationMatrix.yy = 0x10000;
1976 transformationMatrix.xy = 0;
1977 transformationMatrix.yx = 0;
1978 memset(fast_glyph_data, 0, sizeof(fast_glyph_data));
1979 fast_glyph_count = 0;
1982 QFontEngineFT::QGlyphSet::~QGlyphSet()
1987 void QFontEngineFT::QGlyphSet::clear()
1989 if (fast_glyph_count > 0) {
1990 for (int i = 0; i < 256; ++i) {
1991 if (fast_glyph_data[i]) {
1992 delete fast_glyph_data[i];
1993 fast_glyph_data[i] = 0;
1996 fast_glyph_count = 0;
1998 qDeleteAll(glyph_data);
2002 void QFontEngineFT::QGlyphSet::removeGlyphFromCache(glyph_t index, QFixed subPixelPosition)
2004 if (useFastGlyphData(index, subPixelPosition)) {
2005 if (fast_glyph_data[index]) {
2006 delete fast_glyph_data[index];
2007 fast_glyph_data[index] = 0;
2008 if (fast_glyph_count > 0)
2012 delete glyph_data.take(GlyphAndSubPixelPosition(index, subPixelPosition));
2016 void QFontEngineFT::QGlyphSet::setGlyph(glyph_t index, QFixed subPixelPosition, Glyph *glyph)
2018 if (useFastGlyphData(index, subPixelPosition)) {
2019 if (!fast_glyph_data[index])
2021 fast_glyph_data[index] = glyph;
2023 glyph_data.insert(GlyphAndSubPixelPosition(index, subPixelPosition), glyph);
2027 HB_Error QFontEngineFT::getPointInOutline(HB_Glyph glyph, int flags, hb_uint32 point, HB_Fixed *xpos, HB_Fixed *ypos, hb_uint32 *nPoints)
2030 bool hsubpixel = true;
2032 int load_flags = loadFlags(0, Format_A8, flags, hsubpixel, vfactor);
2033 HB_Error result = freetype->getPointInOutline(glyph, load_flags, point, xpos, ypos, nPoints);
2038 bool QFontEngineFT::initFromFontEngine(const QFontEngineFT *fe)
2040 if (!init(fe->faceId(), fe->antialias, fe->defaultFormat, fe->freetype))
2043 // Increase the reference of this QFreetypeFace since one more QFontEngineFT
2045 freetype->ref.ref();
2047 default_load_flags = fe->default_load_flags;
2048 default_hint_style = fe->default_hint_style;
2049 antialias = fe->antialias;
2050 transform = fe->transform;
2051 embolden = fe->embolden;
2052 obliquen = fe->obliquen;
2053 subpixelType = fe->subpixelType;
2054 lcdFilterType = fe->lcdFilterType;
2055 embeddedbitmap = fe->embeddedbitmap;
2060 QFontEngine *QFontEngineFT::cloneWithSize(qreal pixelSize) const
2062 QFontDef fontDef(this->fontDef);
2063 fontDef.pixelSize = pixelSize;
2064 QFontEngineFT *fe = new QFontEngineFT(fontDef);
2065 if (!fe->initFromFontEngine(this)) {
2075 #endif // QT_NO_FREETYPE