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 return QFixed::fromFixed(-metrics.descender);
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();
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