From 626a4d89c2aec6354db9602ad1816e7f7e13d9b1 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Tue, 7 Feb 2017 21:21:59 +0100 Subject: QFormLayout: take the correct row in takeRow() I didn't even try to understand what the old code was trying to do; once you're told by a user that the code is wrong, you see that it is. Fixed by just using the row as passed to takeRow() instead of trying to do some storage-index calculations. The m_matrix indexing operator does it all for us. Added a test that checks that the expected field widget gets returned. Fixed expected test data that was wrong, and just checking that the implementation behaves as implemented, instead of as documented. Amends change 8fbae648db3bc51bafacfe1f88e40561d357e60a. [ChangeLog][QtWidget][QFormLayout] The functions takeRow() and removeRow(), new in 5.8.0, now take and remove the correct row. Task-number: QTBUG-58693 Task-number: QTBUG-15990 Change-Id: I7185ccbc6c03e2579741cad5c0c821d3ed165474 Reviewed-by: Friedemann Kleint --- src/widgets/kernel/qformlayout.cpp | 13 ++++--------- tests/auto/widgets/kernel/qformlayout/tst_qformlayout.cpp | 15 +++++++++------ 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/widgets/kernel/qformlayout.cpp b/src/widgets/kernel/qformlayout.cpp index f3f7280030..6b134379c6 100644 --- a/src/widgets/kernel/qformlayout.cpp +++ b/src/widgets/kernel/qformlayout.cpp @@ -1551,24 +1551,19 @@ QFormLayout::TakeRowResult QFormLayout::takeRow(int row) { Q_D(QFormLayout); - const int storageIndex = storageIndexFromLayoutItem(d->m_matrix, d->m_things.value(row)); - if (Q_UNLIKELY(storageIndex == -1)) { + if (Q_UNLIKELY(!(uint(row) < uint(d->m_matrix.rowCount())))) { qWarning("QFormLayout::takeRow: Invalid row %d", row); return TakeRowResult(); } - int storageRow, dummy; - QFormLayoutPrivate::ItemMatrix::storageIndexToPosition(storageIndex, &storageRow, &dummy); - Q_ASSERT(d->m_matrix(storageRow, dummy)); - - QFormLayoutItem *label = d->m_matrix(storageRow, 0); - QFormLayoutItem *field = d->m_matrix(storageRow, 1); + QFormLayoutItem *label = d->m_matrix(row, 0); + QFormLayoutItem *field = d->m_matrix(row, 1); Q_ASSERT(field); d->m_things.removeOne(label); d->m_things.removeOne(field); - d->m_matrix.removeRow(storageRow); + d->m_matrix.removeRow(row); invalidate(); diff --git a/tests/auto/widgets/kernel/qformlayout/tst_qformlayout.cpp b/tests/auto/widgets/kernel/qformlayout/tst_qformlayout.cpp index d8239b5a28..c324a4bd56 100644 --- a/tests/auto/widgets/kernel/qformlayout/tst_qformlayout.cpp +++ b/tests/auto/widgets/kernel/qformlayout/tst_qformlayout.cpp @@ -757,13 +757,14 @@ void tst_QFormLayout::removeRow() layout->removeRow(1); - QVERIFY(!w1); - QCOMPARE(layout->count(), 1); + QVERIFY(w1); + QVERIFY(!w2); + QCOMPARE(layout->count(), 2); QCOMPARE(layout->rowCount(), 1); layout->removeRow(0); - QVERIFY(!w2); + QVERIFY(!w1); QCOMPARE(layout->count(), 0); QCOMPARE(layout->rowCount(), 0); } @@ -863,17 +864,19 @@ void tst_QFormLayout::takeRow() QVERIFY(w2); QVERIFY(result.fieldItem); - QVERIFY(result.labelItem); - QCOMPARE(layout->count(), 1); + QVERIFY(!result.labelItem); + QCOMPARE(layout->count(), 2); QCOMPARE(layout->rowCount(), 1); + QCOMPARE(result.fieldItem->widget(), w2.data()); result = layout->takeRow(0); QVERIFY(w1); QVERIFY(result.fieldItem); - QVERIFY(!result.labelItem); + QVERIFY(result.labelItem); QCOMPARE(layout->count(), 0); QCOMPARE(layout->rowCount(), 0); + QCOMPARE(result.fieldItem->widget(), w1.data()); result = layout->takeRow(0); -- cgit v1.2.3 From 392b338f15a144b5ec611612b092366c0f42b9f2 Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Thu, 2 Feb 2017 18:00:53 +0100 Subject: move qfontengine_ft.* from gui to platformsupport MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit that's where the files are actually used nowadays. also removes an obsolete (and bogus) gui export. Change-Id: I4551aad798acb6ce8c0abe43a2fcb8e5ac64a2d4 Reviewed-by: Konstantin Ritt Reviewed-by: Friedemann Kleint Reviewed-by: Tor Arne Vestbø Reviewed-by: Joerg Bornemann Reviewed-by: Oswald Buddenhagen --- src/gui/text/qfontengine_ft.cpp | 2190 -------------------- src/gui/text/qfontengine_ft_p.h | 364 ---- src/platformsupport/fontdatabases/basic/basic.pri | 4 +- .../fontdatabases/basic/qbasicfontdatabase.cpp | 3 +- .../fontdatabases/basic/qfontengine_ft.cpp | 2190 ++++++++++++++++++++ .../fontdatabases/basic/qfontengine_ft_p.h | 358 ++++ .../fontconfig/qfontconfigdatabase.cpp | 3 +- .../fontconfig/qfontenginemultifontconfig.cpp | 2 +- src/platformsupport/fontdatabases/mac/coretext.pri | 4 +- .../fontdatabases/mac/qcoretextfontdatabase.mm | 2 +- .../windows/qwindowsfontdatabase_ft.cpp | 3 +- .../fontdatabases/winrt/qwinrtfontdatabase.cpp | 3 +- 12 files changed, 2561 insertions(+), 2565 deletions(-) delete mode 100644 src/gui/text/qfontengine_ft.cpp delete mode 100644 src/gui/text/qfontengine_ft_p.h create mode 100644 src/platformsupport/fontdatabases/basic/qfontengine_ft.cpp create mode 100644 src/platformsupport/fontdatabases/basic/qfontengine_ft_p.h diff --git a/src/gui/text/qfontengine_ft.cpp b/src/gui/text/qfontengine_ft.cpp deleted file mode 100644 index de6da88245..0000000000 --- a/src/gui/text/qfontengine_ft.cpp +++ /dev/null @@ -1,2190 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qdir.h" -#include "qmetatype.h" -#include "qtextstream.h" -#include "qvariant.h" -#include "qfontengine_ft_p.h" -#include "private/qimage_p.h" -#include - -#ifndef QT_NO_FREETYPE - -#include "qfile.h" -#include "qfileinfo.h" -#include -#include "qthreadstorage.h" -#include -#include - -#include -#include FT_FREETYPE_H -#include FT_OUTLINE_H -#include FT_SYNTHESIS_H -#include FT_TRUETYPE_TABLES_H -#include FT_TYPE1_TABLES_H -#include FT_GLYPH_H - -#if defined(FT_LCD_FILTER_H) -#include FT_LCD_FILTER_H -#endif - -#if defined(FT_CONFIG_OPTIONS_H) -#include FT_CONFIG_OPTIONS_H -#endif - -#if defined(FT_LCD_FILTER_H) -#define QT_USE_FREETYPE_LCDFILTER -#endif - -#ifdef QT_LINUXBASE -#include FT_ERRORS_H -#endif - -#if !defined(QT_MAX_CACHED_GLYPH_SIZE) -# define QT_MAX_CACHED_GLYPH_SIZE 64 -#endif - -QT_BEGIN_NAMESPACE - -#define FLOOR(x) ((x) & -64) -#define CEIL(x) (((x)+63) & -64) -#define TRUNC(x) ((x) >> 6) -#define ROUND(x) (((x)+32) & -64) - -static bool ft_getSfntTable(void *user_data, uint tag, uchar *buffer, uint *length) -{ - FT_Face face = (FT_Face)user_data; - - bool result = false; - if (FT_IS_SFNT(face)) { - FT_ULong len = *length; - result = FT_Load_Sfnt_Table(face, tag, 0, buffer, &len) == FT_Err_Ok; - *length = len; - Q_ASSERT(!result || int(*length) > 0); - } - - return result; -} - -static QFontEngineFT::Glyph emptyGlyph = {0, 0, 0, 0, 0, 0, 0, 0}; - -static const QFontEngine::HintStyle ftInitialDefaultHintStyle = -#ifdef Q_OS_WIN - QFontEngineFT::HintFull; -#else - QFontEngineFT::HintNone; -#endif - -// -------------------------- Freetype support ------------------------------ - -class QtFreetypeData -{ -public: - QtFreetypeData() - : library(0) - { } - ~QtFreetypeData(); - - FT_Library library; - QHash faces; -}; - -QtFreetypeData::~QtFreetypeData() -{ - for (QHash::ConstIterator iter = faces.cbegin(); iter != faces.cend(); ++iter) - iter.value()->cleanup(); - faces.clear(); - FT_Done_FreeType(library); - library = 0; -} - -#ifdef QT_NO_THREAD -Q_GLOBAL_STATIC(QtFreetypeData, theFreetypeData) - -QtFreetypeData *qt_getFreetypeData() -{ - return theFreetypeData(); -} -#else -Q_GLOBAL_STATIC(QThreadStorage, theFreetypeData) - -QtFreetypeData *qt_getFreetypeData() -{ - QtFreetypeData *&freetypeData = theFreetypeData()->localData(); - if (!freetypeData) - freetypeData = new QtFreetypeData; - return freetypeData; -} -#endif - -FT_Library qt_getFreetype() -{ - QtFreetypeData *freetypeData = qt_getFreetypeData(); - if (!freetypeData->library) - FT_Init_FreeType(&freetypeData->library); - return freetypeData->library; -} - -int QFreetypeFace::fsType() const -{ - int fsType = 0; - TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(face, ft_sfnt_os2); - if (os2) - fsType = os2->fsType; - return fsType; -} - -int QFreetypeFace::getPointInOutline(glyph_t glyph, int flags, quint32 point, QFixed *xpos, QFixed *ypos, quint32 *nPoints) -{ - if (int error = FT_Load_Glyph(face, glyph, flags)) - return error; - - if (face->glyph->format != FT_GLYPH_FORMAT_OUTLINE) - return Err_Invalid_SubTable; - - *nPoints = face->glyph->outline.n_points; - if (!(*nPoints)) - return Err_Ok; - - if (point > *nPoints) - return Err_Invalid_SubTable; - - *xpos = QFixed::fromFixed(face->glyph->outline.points[point].x); - *ypos = QFixed::fromFixed(face->glyph->outline.points[point].y); - - return Err_Ok; -} - -bool QFreetypeFace::isScalableBitmap() const -{ -#ifdef FT_HAS_COLOR - return !FT_IS_SCALABLE(face) && FT_HAS_COLOR(face); -#else - return false; -#endif -} - -extern QByteArray qt_fontdata_from_index(int); - -/* - * One font file can contain more than one font (bold/italic for example) - * find the right one and return it. - * - * Returns the freetype face or 0 in case of an empty file or any other problems - * (like not being able to open the file) - */ -QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id, - const QByteArray &fontData) -{ - if (face_id.filename.isEmpty() && fontData.isEmpty()) - return 0; - - QtFreetypeData *freetypeData = qt_getFreetypeData(); - if (!freetypeData->library) - FT_Init_FreeType(&freetypeData->library); - - QFreetypeFace *freetype = freetypeData->faces.value(face_id, 0); - if (freetype) { - freetype->ref.ref(); - } else { - QScopedPointer newFreetype(new QFreetypeFace); - FT_Face face; - if (!face_id.filename.isEmpty()) { - QString fileName = QFile::decodeName(face_id.filename); - if (face_id.filename.startsWith(":qmemoryfonts/")) { - // from qfontdatabase.cpp - QByteArray idx = face_id.filename; - idx.remove(0, 14); // remove ':qmemoryfonts/' - bool ok = false; - newFreetype->fontData = qt_fontdata_from_index(idx.toInt(&ok)); - if (!ok) - newFreetype->fontData = QByteArray(); - } else if (!QFileInfo(fileName).isNativePath()) { - QFile file(fileName); - if (!file.open(QIODevice::ReadOnly)) { - return 0; - } - newFreetype->fontData = file.readAll(); - } - } else { - newFreetype->fontData = fontData; - } - if (!newFreetype->fontData.isEmpty()) { - if (FT_New_Memory_Face(freetypeData->library, (const FT_Byte *)newFreetype->fontData.constData(), newFreetype->fontData.size(), face_id.index, &face)) { - return 0; - } - } else if (FT_New_Face(freetypeData->library, face_id.filename, face_id.index, &face)) { - return 0; - } - newFreetype->face = face; - - newFreetype->ref.store(1); - newFreetype->xsize = 0; - newFreetype->ysize = 0; - newFreetype->matrix.xx = 0x10000; - newFreetype->matrix.yy = 0x10000; - newFreetype->matrix.xy = 0; - newFreetype->matrix.yx = 0; - newFreetype->unicode_map = 0; - newFreetype->symbol_map = 0; - - memset(newFreetype->cmapCache, 0, sizeof(newFreetype->cmapCache)); - - for (int i = 0; i < newFreetype->face->num_charmaps; ++i) { - FT_CharMap cm = newFreetype->face->charmaps[i]; - switch(cm->encoding) { - case FT_ENCODING_UNICODE: - newFreetype->unicode_map = cm; - break; - case FT_ENCODING_APPLE_ROMAN: - case FT_ENCODING_ADOBE_LATIN_1: - if (!newFreetype->unicode_map || newFreetype->unicode_map->encoding != FT_ENCODING_UNICODE) - newFreetype->unicode_map = cm; - break; - case FT_ENCODING_ADOBE_CUSTOM: - case FT_ENCODING_MS_SYMBOL: - if (!newFreetype->symbol_map) - newFreetype->symbol_map = cm; - break; - default: - break; - } - } - - if (!FT_IS_SCALABLE(newFreetype->face) && newFreetype->face->num_fixed_sizes == 1) - FT_Set_Char_Size(face, newFreetype->face->available_sizes[0].x_ppem, newFreetype->face->available_sizes[0].y_ppem, 0, 0); - - FT_Set_Charmap(newFreetype->face, newFreetype->unicode_map); - QT_TRY { - freetypeData->faces.insert(face_id, newFreetype.data()); - } QT_CATCH(...) { - newFreetype.take()->release(face_id); - // we could return null in principle instead of throwing - QT_RETHROW; - } - freetype = newFreetype.take(); - } - return freetype; -} - -void QFreetypeFace::cleanup() -{ - hbFace.reset(); - FT_Done_Face(face); - face = 0; -} - -void QFreetypeFace::release(const QFontEngine::FaceId &face_id) -{ - if (!ref.deref()) { - if (face) { - QtFreetypeData *freetypeData = qt_getFreetypeData(); - - cleanup(); - - auto it = freetypeData->faces.constFind(face_id); - if (it != freetypeData->faces.constEnd()) - freetypeData->faces.erase(it); - - if (freetypeData->faces.isEmpty()) { - FT_Done_FreeType(freetypeData->library); - freetypeData->library = 0; - } - } - - delete this; - } -} - - -void QFreetypeFace::computeSize(const QFontDef &fontDef, int *xsize, int *ysize, bool *outline_drawing, QFixed *scalableBitmapScaleFactor) -{ - *ysize = qRound(fontDef.pixelSize * 64); - *xsize = *ysize * fontDef.stretch / 100; - *scalableBitmapScaleFactor = 1; - *outline_drawing = false; - - if (!(face->face_flags & FT_FACE_FLAG_SCALABLE)) { - int best = 0; - if (!isScalableBitmap()) { - /* - * Bitmap only faces must match exactly, so find the closest - * one (height dominant search) - */ - for (int i = 1; i < face->num_fixed_sizes; i++) { - if (qAbs(*ysize - face->available_sizes[i].y_ppem) < - qAbs(*ysize - face->available_sizes[best].y_ppem) || - (qAbs(*ysize - face->available_sizes[i].y_ppem) == - qAbs(*ysize - face->available_sizes[best].y_ppem) && - qAbs(*xsize - face->available_sizes[i].x_ppem) < - qAbs(*xsize - face->available_sizes[best].x_ppem))) { - best = i; - } - } - } else { - // Select the shortest bitmap strike whose height is larger than the desired height - for (int i = 1; i < face->num_fixed_sizes; i++) { - if (face->available_sizes[i].y_ppem < *ysize) { - if (face->available_sizes[i].y_ppem > face->available_sizes[best].y_ppem) - best = i; - } else if (face->available_sizes[best].y_ppem < *ysize) { - best = i; - } else if (face->available_sizes[i].y_ppem < face->available_sizes[best].y_ppem) { - best = i; - } - } - } - - // According to freetype documentation we must use FT_Select_Size - // to make sure we can select the desired bitmap strike index - if (FT_Select_Size(face, best) == 0) { - if (isScalableBitmap()) - *scalableBitmapScaleFactor = QFixed::fromReal((qreal)fontDef.pixelSize / face->available_sizes[best].height); - *xsize = face->available_sizes[best].x_ppem; - *ysize = face->available_sizes[best].y_ppem; - } else { - *xsize = *ysize = 0; - } - } else { - *outline_drawing = (*xsize > (QT_MAX_CACHED_GLYPH_SIZE<<6) || *ysize > (QT_MAX_CACHED_GLYPH_SIZE<<6)); - } -} - -QFontEngine::Properties QFreetypeFace::properties() const -{ - QFontEngine::Properties p; - p.postscriptName = FT_Get_Postscript_Name(face); - PS_FontInfoRec font_info; - if (FT_Get_PS_Font_Info(face, &font_info) == 0) - p.copyright = font_info.notice; - if (FT_IS_SCALABLE(face)) { - p.ascent = face->ascender; - p.descent = -face->descender; - p.leading = face->height - face->ascender + face->descender; - p.emSquare = face->units_per_EM; - p.boundingBox = QRectF(face->bbox.xMin, -face->bbox.yMax, - face->bbox.xMax - face->bbox.xMin, - face->bbox.yMax - face->bbox.yMin); - } else { - p.ascent = QFixed::fromFixed(face->size->metrics.ascender); - p.descent = QFixed::fromFixed(-face->size->metrics.descender); - p.leading = QFixed::fromFixed(face->size->metrics.height - face->size->metrics.ascender + face->size->metrics.descender); - p.emSquare = face->size->metrics.y_ppem; -// p.boundingBox = QRectF(-p.ascent.toReal(), 0, (p.ascent + p.descent).toReal(), face->size->metrics.max_advance/64.); - p.boundingBox = QRectF(0, -p.ascent.toReal(), - face->size->metrics.max_advance/64, (p.ascent + p.descent).toReal() ); - } - p.italicAngle = 0; - p.capHeight = p.ascent; - p.lineWidth = face->underline_thickness; - - return p; -} - -bool QFreetypeFace::getSfntTable(uint tag, uchar *buffer, uint *length) const -{ - return ft_getSfntTable(face, tag, buffer, length); -} - -/* Some fonts (such as MingLiu rely on hinting to scale different - components to their correct sizes. While this is really broken (it - should be done in the component glyph itself, not the hinter) we - will have to live with it. - - This means we can not use FT_LOAD_NO_HINTING to get the glyph - outline. All we can do is to load the unscaled glyph and scale it - down manually when required. -*/ -static void scaleOutline(FT_Face face, FT_GlyphSlot g, FT_Fixed x_scale, FT_Fixed y_scale) -{ - x_scale = FT_MulDiv(x_scale, 1 << 10, face->units_per_EM); - y_scale = FT_MulDiv(y_scale, 1 << 10, face->units_per_EM); - FT_Vector *p = g->outline.points; - const FT_Vector *e = p + g->outline.n_points; - while (p < e) { - p->x = FT_MulFix(p->x, x_scale); - p->y = FT_MulFix(p->y, y_scale); - ++p; - } -} - -#define GLYPH2PATH_DEBUG QT_NO_QDEBUG_MACRO // qDebug -void QFreetypeFace::addGlyphToPath(FT_Face face, FT_GlyphSlot g, const QFixedPoint &point, QPainterPath *path, FT_Fixed x_scale, FT_Fixed y_scale) -{ - const qreal factor = 1/64.; - scaleOutline(face, g, x_scale, y_scale); - - QPointF cp = point.toPointF(); - - // convert the outline to a painter path - int i = 0; - for (int j = 0; j < g->outline.n_contours; ++j) { - int last_point = g->outline.contours[j]; - GLYPH2PATH_DEBUG() << "contour:" << i << "to" << last_point; - QPointF start = QPointF(g->outline.points[i].x*factor, -g->outline.points[i].y*factor); - if (!(g->outline.tags[i] & 1)) { // start point is not on curve: - if (!(g->outline.tags[last_point] & 1)) { // end point is not on curve: - GLYPH2PATH_DEBUG() << " start and end point are not on curve"; - start = (QPointF(g->outline.points[last_point].x*factor, - -g->outline.points[last_point].y*factor) + start) / 2.0; - } else { - GLYPH2PATH_DEBUG() << " end point is on curve, start is not"; - start = QPointF(g->outline.points[last_point].x*factor, - -g->outline.points[last_point].y*factor); - } - --i; // to use original start point as control point below - } - start += cp; - GLYPH2PATH_DEBUG() << " start at" << start; - - path->moveTo(start); - QPointF c[4]; - c[0] = start; - int n = 1; - while (i < last_point) { - ++i; - c[n] = cp + QPointF(g->outline.points[i].x*factor, -g->outline.points[i].y*factor); - GLYPH2PATH_DEBUG() << " " << i << c[n] << "tag =" << (int)g->outline.tags[i] - << ": on curve =" << (bool)(g->outline.tags[i] & 1); - ++n; - switch (g->outline.tags[i] & 3) { - case 2: - // cubic bezier element - if (n < 4) - continue; - c[3] = (c[3] + c[2])/2; - --i; - break; - case 0: - // quadratic bezier element - if (n < 3) - continue; - c[3] = (c[1] + c[2])/2; - c[2] = (2*c[1] + c[3])/3; - c[1] = (2*c[1] + c[0])/3; - --i; - break; - case 1: - case 3: - if (n == 2) { - GLYPH2PATH_DEBUG() << " lineTo" << c[1]; - path->lineTo(c[1]); - c[0] = c[1]; - n = 1; - continue; - } else if (n == 3) { - c[3] = c[2]; - c[2] = (2*c[1] + c[3])/3; - c[1] = (2*c[1] + c[0])/3; - } - break; - } - GLYPH2PATH_DEBUG() << " cubicTo" << c[1] << c[2] << c[3]; - path->cubicTo(c[1], c[2], c[3]); - c[0] = c[3]; - n = 1; - } - - if (n == 1) { - GLYPH2PATH_DEBUG() << " closeSubpath"; - path->closeSubpath(); - } else { - c[3] = start; - if (n == 2) { - c[2] = (2*c[1] + c[3])/3; - c[1] = (2*c[1] + c[0])/3; - } - GLYPH2PATH_DEBUG() << " close cubicTo" << c[1] << c[2] << c[3]; - path->cubicTo(c[1], c[2], c[3]); - } - ++i; - } -} - -extern void qt_addBitmapToPath(qreal x0, qreal y0, const uchar *image_data, int bpl, int w, int h, QPainterPath *path); - -void QFreetypeFace::addBitmapToPath(FT_GlyphSlot slot, const QFixedPoint &point, QPainterPath *path) -{ - if (slot->format != FT_GLYPH_FORMAT_BITMAP - || slot->bitmap.pixel_mode != FT_PIXEL_MODE_MONO) - return; - - QPointF cp = point.toPointF(); - qt_addBitmapToPath(cp.x() + TRUNC(slot->metrics.horiBearingX), cp.y() - TRUNC(slot->metrics.horiBearingY), - slot->bitmap.buffer, slot->bitmap.pitch, slot->bitmap.width, slot->bitmap.rows, path); -} - -QFontEngineFT::Glyph::~Glyph() -{ - delete [] data; -} - -struct LcdFilterDummy -{ - static inline void filterPixel(uchar &, uchar &, uchar &) - {} -}; - -struct LcdFilterLegacy -{ - static inline void filterPixel(uchar &red, uchar &green, uchar &blue) - { - uint r = red, g = green, b = blue; - // intra-pixel filter used by the legacy filter (adopted from _ft_lcd_filter_legacy) - red = (r * uint(65538 * 9/13) + g * uint(65538 * 1/6) + b * uint(65538 * 1/13)) / 65536; - green = (r * uint(65538 * 3/13) + g * uint(65538 * 4/6) + b * uint(65538 * 3/13)) / 65536; - blue = (r * uint(65538 * 1/13) + g * uint(65538 * 1/6) + b * uint(65538 * 9/13)) / 65536; - } -}; - -template -static void convertRGBToARGB_helper(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr) -{ - const int offs = bgr ? -1 : 1; - const int w = width * 3; - while (height--) { - uint *dd = dst; - for (int x = 0; x < w; x += 3) { - uchar red = src[x + 1 - offs]; - uchar green = src[x + 1]; - uchar blue = src[x + 1 + offs]; - LcdFilter::filterPixel(red, green, blue); - *dd++ = (0xFF << 24) | (red << 16) | (green << 8) | blue; - } - dst += width; - src += src_pitch; - } -} - -static inline void convertRGBToARGB(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr, bool legacyFilter) -{ - if (!legacyFilter) - convertRGBToARGB_helper(src, dst, width, height, src_pitch, bgr); - else - convertRGBToARGB_helper(src, dst, width, height, src_pitch, bgr); -} - -template -static void convertRGBToARGB_V_helper(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr) -{ - const int offs = bgr ? -src_pitch : src_pitch; - while (height--) { - for (int x = 0; x < width; x++) { - uchar red = src[x + src_pitch - offs]; - uchar green = src[x + src_pitch]; - uchar blue = src[x + src_pitch + offs]; - LcdFilter::filterPixel(red, green, blue); - *dst++ = (0XFF << 24) | (red << 16) | (green << 8) | blue; - } - src += 3*src_pitch; - } -} - -static inline void convertRGBToARGB_V(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr, bool legacyFilter) -{ - if (!legacyFilter) - convertRGBToARGB_V_helper(src, dst, width, height, src_pitch, bgr); - else - convertRGBToARGB_V_helper(src, dst, width, height, src_pitch, bgr); -} - -static inline void convertGRAYToARGB(const uchar *src, uint *dst, int width, int height, int src_pitch) -{ - while (height--) { - const uchar *p = src; - const uchar * const e = p + width; - while (p < e) { - uchar gray = *p++; - *dst++ = (0xFF << 24) | (gray << 16) | (gray << 8) | gray; - } - src += src_pitch; - } -} - -static void convoluteBitmap(const uchar *src, uchar *dst, int width, int height, int pitch) -{ - // convolute the bitmap with a triangle filter to get rid of color fringes - // If we take account for a gamma value of 2, we end up with - // weights of 1, 4, 9, 4, 1. We use an approximation of 1, 3, 8, 3, 1 here, - // as this nicely sums up to 16 :) - int h = height; - while (h--) { - dst[0] = dst[1] = 0; - // - for (int x = 2; x < width - 2; ++x) { - uint sum = src[x-2] + 3*src[x-1] + 8*src[x] + 3*src[x+1] + src[x+2]; - dst[x] = (uchar) (sum >> 4); - } - dst[width - 2] = dst[width - 1] = 0; - src += pitch; - dst += pitch; - } -} - -QFontEngineFT::QFontEngineFT(const QFontDef &fd) - : QFontEngine(Freetype) -{ - fontDef = fd; - matrix.xx = 0x10000; - matrix.yy = 0x10000; - matrix.xy = 0; - matrix.yx = 0; - cache_cost = 100 * 1024; - kerning_pairs_loaded = false; - transform = false; - embolden = false; - obliquen = false; - antialias = true; - freetype = 0; - default_load_flags = FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH; - default_hint_style = ftInitialDefaultHintStyle; - subpixelType = Subpixel_None; - lcdFilterType = 0; -#if defined(FT_LCD_FILTER_H) - lcdFilterType = (int)((quintptr) FT_LCD_FILTER_DEFAULT); -#endif - defaultFormat = Format_None; - embeddedbitmap = false; - const QByteArray env = qgetenv("QT_NO_FT_CACHE"); - cacheEnabled = env.isEmpty() || env.toInt() == 0; - m_subPixelPositionCount = 4; - forceAutoHint = false; -} - -QFontEngineFT::~QFontEngineFT() -{ - if (freetype) - freetype->release(face_id); -} - -bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format, - const QByteArray &fontData) -{ - return init(faceId, antialias, format, QFreetypeFace::getFace(faceId, fontData)); -} - -static void dont_delete(void*) {} - -bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format, - QFreetypeFace *freetypeFace) -{ - freetype = freetypeFace; - if (!freetype) { - xsize = 0; - ysize = 0; - return false; - } - defaultFormat = format; - this->antialias = antialias; - - if (!antialias) - glyphFormat = QFontEngine::Format_Mono; - else - glyphFormat = defaultFormat; - - face_id = faceId; - - symbol = freetype->symbol_map != 0; - PS_FontInfoRec psrec; - // don't assume that type1 fonts are symbol fonts by default - if (FT_Get_PS_Font_Info(freetype->face, &psrec) == FT_Err_Ok) { - symbol = bool(fontDef.family.contains(QLatin1String("symbol"), Qt::CaseInsensitive)); - } - - freetype->computeSize(fontDef, &xsize, &ysize, &defaultGlyphSet.outline_drawing, &scalableBitmapScaleFactor); - - FT_Face face = lockFace(); - - if (FT_IS_SCALABLE(face)) { - bool fake_oblique = (fontDef.style != QFont::StyleNormal) && !(face->style_flags & FT_STYLE_FLAG_ITALIC); - if (fake_oblique) - obliquen = true; - FT_Set_Transform(face, &matrix, 0); - freetype->matrix = matrix; - // fake bold - if ((fontDef.weight >= QFont::Bold) && !(face->style_flags & FT_STYLE_FLAG_BOLD) && !FT_IS_FIXED_WIDTH(face)) { - if (const TT_OS2 *os2 = reinterpret_cast(FT_Get_Sfnt_Table(face, ft_sfnt_os2))) { - if (os2->usWeightClass < 750) - embolden = true; - } - } - // underline metrics - line_thickness = QFixed::fromFixed(FT_MulFix(face->underline_thickness, face->size->metrics.y_scale)); - underline_position = QFixed::fromFixed(-FT_MulFix(face->underline_position, face->size->metrics.y_scale)); - } else { - // ad hoc algorithm - int score = fontDef.weight * fontDef.pixelSize; - line_thickness = score / 700; - // looks better with thicker line for small pointsizes - if (line_thickness < 2 && score >= 1050) - line_thickness = 2; - underline_position = ((line_thickness * 2) + 3) / 6; - - if (isScalableBitmap()) { - glyphFormat = defaultFormat = GlyphFormat::Format_ARGB; - cacheEnabled = false; - } - } - if (line_thickness < 1) - line_thickness = 1; - - metrics = face->size->metrics; - - /* - TrueType fonts with embedded bitmaps may have a bitmap font specific - ascent/descent in the EBLC table. There is no direct public API - to extract those values. The only way we've found is to trick freetype - into thinking that it's not a scalable font in FT_SelectSize so that - the metrics are retrieved from the bitmap strikes. - */ - if (FT_IS_SCALABLE(face)) { - for (int i = 0; i < face->num_fixed_sizes; ++i) { - if (xsize == face->available_sizes[i].x_ppem && ysize == face->available_sizes[i].y_ppem) { - face->face_flags &= ~FT_FACE_FLAG_SCALABLE; - - FT_Select_Size(face, i); - if (face->size->metrics.ascender + face->size->metrics.descender > 0) { - FT_Pos leading = metrics.height - metrics.ascender + metrics.descender; - metrics.ascender = face->size->metrics.ascender; - metrics.descender = face->size->metrics.descender; - if (metrics.descender > 0 - && QString::fromUtf8(face->family_name) == QLatin1String("Courier New")) { - metrics.descender *= -1; - } - metrics.height = metrics.ascender - metrics.descender + leading; - } - FT_Set_Char_Size(face, xsize, ysize, 0, 0); - - face->face_flags |= FT_FACE_FLAG_SCALABLE; - break; - } - } - } - - fontDef.styleName = QString::fromUtf8(face->style_name); - - if (!freetype->hbFace) { - faceData.user_data = face; - faceData.get_font_table = ft_getSfntTable; - (void)harfbuzzFace(); // populates face_ - freetype->hbFace = std::move(face_); - } else { - Q_ASSERT(!face_); - } - // we share the HB face in QFreeTypeFace, so do not let ~QFontEngine() destroy it - face_ = Holder(freetype->hbFace.get(), dont_delete); - - unlockFace(); - - fsType = freetype->fsType(); - return true; -} - -void QFontEngineFT::setQtDefaultHintStyle(QFont::HintingPreference hintingPreference) -{ - switch (hintingPreference) { - case QFont::PreferNoHinting: - setDefaultHintStyle(HintNone); - break; - case QFont::PreferFullHinting: - setDefaultHintStyle(HintFull); - break; - case QFont::PreferVerticalHinting: - setDefaultHintStyle(HintLight); - break; - case QFont::PreferDefaultHinting: - setDefaultHintStyle(ftInitialDefaultHintStyle); - break; - } -} - -void QFontEngineFT::setDefaultHintStyle(HintStyle style) -{ - default_hint_style = style; -} - -int QFontEngineFT::loadFlags(QGlyphSet *set, GlyphFormat format, int flags, - bool &hsubpixel, int &vfactor) const -{ - int load_flags = FT_LOAD_DEFAULT | default_load_flags; - int load_target = default_hint_style == HintLight - ? FT_LOAD_TARGET_LIGHT - : FT_LOAD_TARGET_NORMAL; - - if (format == Format_Mono) { - load_target = FT_LOAD_TARGET_MONO; - } else if (format == Format_A32) { - if (subpixelType == Subpixel_RGB || subpixelType == Subpixel_BGR) { - if (default_hint_style == HintFull) - load_target = FT_LOAD_TARGET_LCD; - hsubpixel = true; - } else if (subpixelType == Subpixel_VRGB || subpixelType == Subpixel_VBGR) { - if (default_hint_style == HintFull) - load_target = FT_LOAD_TARGET_LCD_V; - vfactor = 3; - } - } else if (format == Format_ARGB) { -#ifdef FT_LOAD_COLOR - load_flags |= FT_LOAD_COLOR; -#endif - } - - if (set && set->outline_drawing) - load_flags |= FT_LOAD_NO_BITMAP; - - if (default_hint_style == HintNone || (flags & DesignMetrics) || (set && set->outline_drawing)) - load_flags |= FT_LOAD_NO_HINTING; - else - load_flags |= load_target; - - if (forceAutoHint) - load_flags |= FT_LOAD_FORCE_AUTOHINT; - - return load_flags; -} - -static inline bool areMetricsTooLarge(const QFontEngineFT::GlyphInfo &info) -{ - // false if exceeds QFontEngineFT::Glyph metrics - return (short)(info.linearAdvance) != info.linearAdvance - || (uchar)(info.width) != info.width - || (uchar)(info.height) != info.height; -} - -static inline void transformBoundingBox(int *left, int *top, int *right, int *bottom, FT_Matrix *matrix) -{ - int l, r, t, b; - FT_Vector vector; - vector.x = *left; - vector.y = *top; - FT_Vector_Transform(&vector, matrix); - l = r = vector.x; - t = b = vector.y; - vector.x = *right; - vector.y = *top; - FT_Vector_Transform(&vector, matrix); - if (l > vector.x) l = vector.x; - if (r < vector.x) r = vector.x; - if (t < vector.y) t = vector.y; - if (b > vector.y) b = vector.y; - vector.x = *right; - vector.y = *bottom; - FT_Vector_Transform(&vector, matrix); - if (l > vector.x) l = vector.x; - if (r < vector.x) r = vector.x; - if (t < vector.y) t = vector.y; - if (b > vector.y) b = vector.y; - vector.x = *left; - vector.y = *bottom; - FT_Vector_Transform(&vector, matrix); - if (l > vector.x) l = vector.x; - if (r < vector.x) r = vector.x; - if (t < vector.y) t = vector.y; - if (b > vector.y) b = vector.y; - *left = l; - *right = r; - *top = t; - *bottom = b; -} - -QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, - QFixed subPixelPosition, - GlyphFormat format, - bool fetchMetricsOnly, - bool disableOutlineDrawing) const -{ -// Q_ASSERT(freetype->lock == 1); - - if (format == Format_None) - format = defaultFormat != Format_None ? defaultFormat : Format_Mono; - Q_ASSERT(format != Format_None); - - Glyph *g = set ? set->getGlyph(glyph, subPixelPosition) : 0; - if (g && g->format == format && (fetchMetricsOnly || g->data)) - return g; - - if (!g && set && set->isGlyphMissing(glyph)) - return &emptyGlyph; - - - FT_Face face = freetype->face; - - FT_Matrix matrix = freetype->matrix; - - FT_Vector v; - v.x = format == Format_Mono ? 0 : FT_Pos(subPixelPosition.value()); - v.y = 0; - FT_Set_Transform(face, &matrix, &v); - - bool hsubpixel = false; - int vfactor = 1; - int load_flags = loadFlags(set, format, 0, hsubpixel, vfactor); - - bool transform = matrix.xx != 0x10000 - || matrix.yy != 0x10000 - || matrix.xy != 0 - || matrix.yx != 0; - - if (transform || (format != Format_Mono && !isScalableBitmap())) - load_flags |= FT_LOAD_NO_BITMAP; - - FT_Error err = FT_Load_Glyph(face, glyph, load_flags); - if (err && (load_flags & FT_LOAD_NO_BITMAP)) { - load_flags &= ~FT_LOAD_NO_BITMAP; - err = FT_Load_Glyph(face, glyph, load_flags); - } - if (err == FT_Err_Too_Few_Arguments) { - // this is an error in the bytecode interpreter, just try to run without it - load_flags |= FT_LOAD_FORCE_AUTOHINT; - err = FT_Load_Glyph(face, glyph, load_flags); - } else if (err == FT_Err_Execution_Too_Long) { - // This is an error in the bytecode, probably a web font made by someone who - // didn't test bytecode hinting at all so disable for it for all glyphs. - qWarning("load glyph failed due to broken hinting bytecode in font, switching to auto hinting"); - default_load_flags |= FT_LOAD_FORCE_AUTOHINT; - load_flags |= FT_LOAD_FORCE_AUTOHINT; - err = FT_Load_Glyph(face, glyph, load_flags); - } - if (err != FT_Err_Ok) { - qWarning("load glyph failed err=%x face=%p, glyph=%d", err, face, glyph); - if (set) - set->setGlyphMissing(glyph); - return &emptyGlyph; - } - - FT_GlyphSlot slot = face->glyph; - - if (embolden) - FT_GlyphSlot_Embolden(slot); - if (obliquen) { - FT_GlyphSlot_Oblique(slot); - - // While Embolden alters the metrics of the slot, oblique does not, so we need - // to fix this ourselves. - transform = true; - FT_Matrix m; - m.xx = 0x10000; - m.yx = 0x0; - m.xy = 0x6000; - m.yy = 0x10000; - - FT_Matrix_Multiply(&m, &matrix); - } - - GlyphInfo info; - info.linearAdvance = slot->linearHoriAdvance >> 10; - info.xOff = TRUNC(ROUND(slot->advance.x)); - info.yOff = 0; - - if ((set && set->outline_drawing && !disableOutlineDrawing) || fetchMetricsOnly) { - int left = slot->metrics.horiBearingX; - int right = slot->metrics.horiBearingX + slot->metrics.width; - int top = slot->metrics.horiBearingY; - int bottom = slot->metrics.horiBearingY - slot->metrics.height; - - if (transform && slot->format != FT_GLYPH_FORMAT_BITMAP) - transformBoundingBox(&left, &top, &right, &bottom, &matrix); - - left = FLOOR(left); - right = CEIL(right); - bottom = FLOOR(bottom); - top = CEIL(top); - - info.x = TRUNC(left); - info.y = TRUNC(top); - info.width = TRUNC(right - left); - info.height = TRUNC(top - bottom); - - // If any of the metrics are too large to fit, don't cache them - if (areMetricsTooLarge(info)) - return 0; - - g = new Glyph; - g->data = 0; - g->linearAdvance = info.linearAdvance; - g->width = info.width; - g->height = info.height; - g->x = info.x; - g->y = info.y; - g->advance = info.xOff; - g->format = format; - - if (set) - set->setGlyph(glyph, subPixelPosition, g); - - return g; - } - - int glyph_buffer_size = 0; - QScopedArrayPointer glyph_buffer; -#if defined(QT_USE_FREETYPE_LCDFILTER) - bool useFreetypeRenderGlyph = false; - if (slot->format == FT_GLYPH_FORMAT_OUTLINE && (hsubpixel || vfactor != 1)) { - err = FT_Library_SetLcdFilter(slot->library, (FT_LcdFilter)lcdFilterType); - if (err == FT_Err_Ok) - useFreetypeRenderGlyph = true; - } - - if (useFreetypeRenderGlyph) { - err = FT_Render_Glyph(slot, hsubpixel ? FT_RENDER_MODE_LCD : FT_RENDER_MODE_LCD_V); - - if (err != FT_Err_Ok) - qWarning("render glyph failed err=%x face=%p, glyph=%d", err, face, glyph); - - FT_Library_SetLcdFilter(slot->library, FT_LCD_FILTER_NONE); - - info.height = slot->bitmap.rows / vfactor; - info.width = hsubpixel ? slot->bitmap.width / 3 : slot->bitmap.width; - info.x = slot->bitmap_left; - info.y = slot->bitmap_top; - - glyph_buffer_size = info.width * info.height * 4; - glyph_buffer.reset(new uchar[glyph_buffer_size]); - - if (hsubpixel) - convertRGBToARGB(slot->bitmap.buffer, (uint *)glyph_buffer.data(), info.width, info.height, slot->bitmap.pitch, subpixelType != Subpixel_RGB, false); - else if (vfactor != 1) - convertRGBToARGB_V(slot->bitmap.buffer, (uint *)glyph_buffer.data(), info.width, info.height, slot->bitmap.pitch, subpixelType != Subpixel_VRGB, false); - } else -#endif - { - int left = slot->metrics.horiBearingX; - int right = slot->metrics.horiBearingX + slot->metrics.width; - int top = slot->metrics.horiBearingY; - int bottom = slot->metrics.horiBearingY - slot->metrics.height; - if (transform && slot->format != FT_GLYPH_FORMAT_BITMAP) - transformBoundingBox(&left, &top, &right, &bottom, &matrix); - left = FLOOR(left); - right = CEIL(right); - bottom = FLOOR(bottom); - top = CEIL(top); - - int hpixels = TRUNC(right - left); - // subpixel position requires one more pixel - if (subPixelPosition > 0 && format != Format_Mono) - hpixels++; - - if (hsubpixel) - hpixels = hpixels*3 + 8; - info.width = hpixels; - info.height = TRUNC(top - bottom); - info.x = TRUNC(left); - info.y = TRUNC(top); - if (hsubpixel) { - info.width /= 3; - info.x -= 1; - } - - // If any of the metrics are too large to fit, don't cache them - if (areMetricsTooLarge(info)) - return 0; - - int pitch = (format == Format_Mono ? ((info.width + 31) & ~31) >> 3 : - (format == Format_A8 ? (info.width + 3) & ~3 : info.width * 4)); - if (glyph_buffer_size < pitch * info.height) { - glyph_buffer_size = pitch * info.height; - glyph_buffer.reset(new uchar[glyph_buffer_size]); - memset(glyph_buffer.data(), 0, glyph_buffer_size); - } - - if (slot->format == FT_GLYPH_FORMAT_OUTLINE) { - FT_Bitmap bitmap; - bitmap.rows = info.height*vfactor; - bitmap.width = hpixels; - bitmap.pitch = format == Format_Mono ? (((info.width + 31) & ~31) >> 3) : ((bitmap.width + 3) & ~3); - int bitmap_buffer_size = bitmap.rows * bitmap.pitch; - if (!hsubpixel && vfactor == 1 && format != Format_A32) { - Q_ASSERT(glyph_buffer_size <= bitmap_buffer_size); - bitmap.buffer = glyph_buffer.data(); - } else { - bitmap.buffer = new uchar[bitmap_buffer_size]; - memset(bitmap.buffer, 0, bitmap_buffer_size); - } - bitmap.pixel_mode = format == Format_Mono ? FT_PIXEL_MODE_MONO : FT_PIXEL_MODE_GRAY; - FT_Matrix matrix; - matrix.xx = (hsubpixel ? 3 : 1) << 16; - matrix.yy = vfactor << 16; - matrix.yx = matrix.xy = 0; - - FT_Outline_Transform(&slot->outline, &matrix); - FT_Outline_Translate (&slot->outline, (hsubpixel ? -3*left +(4<<6) : -left), -bottom*vfactor); - FT_Outline_Get_Bitmap(slot->library, &slot->outline, &bitmap); - if (hsubpixel) { - Q_ASSERT (bitmap.pixel_mode == FT_PIXEL_MODE_GRAY); - Q_ASSERT(antialias); - uchar *convoluted = new uchar[bitmap_buffer_size]; - bool useLegacyLcdFilter = false; -#if defined(FC_LCD_FILTER) && defined(FT_LCD_FILTER_H) - useLegacyLcdFilter = (lcdFilterType == FT_LCD_FILTER_LEGACY); -#endif - uchar *buffer = bitmap.buffer; - if (!useLegacyLcdFilter) { - convoluteBitmap(bitmap.buffer, convoluted, bitmap.width, info.height, bitmap.pitch); - buffer = convoluted; - } - convertRGBToARGB(buffer + 1, (uint *)glyph_buffer.data(), info.width, info.height, bitmap.pitch, subpixelType != Subpixel_RGB, useLegacyLcdFilter); - delete [] convoluted; - } else if (vfactor != 1) { - convertRGBToARGB_V(bitmap.buffer, (uint *)glyph_buffer.data(), info.width, info.height, bitmap.pitch, subpixelType != Subpixel_VRGB, true); - } else if (format == Format_A32 && bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) { - convertGRAYToARGB(bitmap.buffer, (uint *)glyph_buffer.data(), info.width, info.height, bitmap.pitch); - } - - if (bitmap.buffer != glyph_buffer.data()) - delete [] bitmap.buffer; - } else if (slot->format == FT_GLYPH_FORMAT_BITMAP) { -#if ((FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100) >= 20500) - Q_ASSERT(slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO || slot->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA); -#else - Q_ASSERT(slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO); -#endif - uchar *src = slot->bitmap.buffer; - uchar *dst = glyph_buffer.data(); - int h = slot->bitmap.rows; - if (format == Format_Mono) { - int bytes = ((info.width + 7) & ~7) >> 3; - while (h--) { - memcpy (dst, src, bytes); - dst += pitch; - src += slot->bitmap.pitch; - } - } else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) { - if (hsubpixel) { - while (h--) { - uint *dd = (uint *)dst; - *dd++ = 0; - for (int x = 0; x < static_cast(slot->bitmap.width); x++) { - uint a = ((src[x >> 3] & (0x80 >> (x & 7))) ? 0xffffff : 0x000000); - *dd++ = a; - } - *dd++ = 0; - dst += pitch; - src += slot->bitmap.pitch; - } - } else if (vfactor != 1) { - while (h--) { - uint *dd = (uint *)dst; - for (int x = 0; x < static_cast(slot->bitmap.width); x++) { - uint a = ((src[x >> 3] & (0x80 >> (x & 7))) ? 0xffffff : 0x000000); - *dd++ = a; - } - dst += pitch; - src += slot->bitmap.pitch; - } - } else { - while (h--) { - for (int x = 0; x < static_cast(slot->bitmap.width); x++) { - unsigned char a = ((src[x >> 3] & (0x80 >> (x & 7))) ? 0xff : 0x00); - dst[x] = a; - } - dst += pitch; - src += slot->bitmap.pitch; - } - } - } -#if ((FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100) >= 20500) - else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) - { - while (h--) { -#if Q_BYTE_ORDER == Q_BIG_ENDIAN - const quint32 *srcPixel = (const quint32 *)src; - quint32 *dstPixel = (quint32 *)dst; - for (int x = 0; x < static_cast(slot->bitmap.width); x++, srcPixel++, dstPixel++) { - const quint32 pixel = *srcPixel; - *dstPixel = qbswap(pixel); - } -#else - memcpy(dst, src, slot->bitmap.width * 4); -#endif - dst += slot->bitmap.pitch; - src += slot->bitmap.pitch; - } - info.width = info.linearAdvance = info.xOff = slot->bitmap.width; - info.height = slot->bitmap.rows; - info.x = slot->bitmap_left; - info.y = slot->bitmap_top; - } -#endif - } else { - qWarning("QFontEngine: Glyph neither outline nor bitmap format=%d", slot->format); - return 0; - } - } - - - if (!g) { - g = new Glyph; - g->data = 0; - } - - g->linearAdvance = info.linearAdvance; - g->width = info.width; - g->height = info.height; - g->x = info.x; - g->y = info.y; - g->advance = info.xOff; - g->format = format; - delete [] g->data; - g->data = glyph_buffer.take(); - - if (set) - set->setGlyph(glyph, subPixelPosition, g); - - return g; -} - -QFontEngine::FaceId QFontEngineFT::faceId() const -{ - return face_id; -} - -QFontEngine::Properties QFontEngineFT::properties() const -{ - Properties p = freetype->properties(); - if (p.postscriptName.isEmpty()) { - p.postscriptName = QFontEngine::convertToPostscriptFontFamilyName(fontDef.family.toUtf8()); - } - - return freetype->properties(); -} - -QFixed QFontEngineFT::emSquareSize() const -{ - if (FT_IS_SCALABLE(freetype->face)) - return freetype->face->units_per_EM; - else - return freetype->face->size->metrics.y_ppem; -} - -bool QFontEngineFT::getSfntTableData(uint tag, uchar *buffer, uint *length) const -{ - return ft_getSfntTable(freetype->face, tag, buffer, length); -} - -int QFontEngineFT::synthesized() const -{ - int s = 0; - if ((fontDef.style != QFont::StyleNormal) && !(freetype->face->style_flags & FT_STYLE_FLAG_ITALIC)) - s = SynthesizedItalic; - if ((fontDef.weight >= QFont::Bold) && !(freetype->face->style_flags & FT_STYLE_FLAG_BOLD)) - s |= SynthesizedBold; - if (fontDef.stretch != 100 && FT_IS_SCALABLE(freetype->face)) - s |= SynthesizedStretch; - return s; -} - -QFixed QFontEngineFT::ascent() const -{ - QFixed v = QFixed::fromFixed(metrics.ascender); - if (scalableBitmapScaleFactor != 1) - v *= scalableBitmapScaleFactor; - return v; -} - -QFixed QFontEngineFT::capHeight() const -{ - TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2); - if (os2 && os2->version >= 2) { - lockFace(); - QFixed answer = QFixed::fromFixed(FT_MulFix(os2->sCapHeight, freetype->face->size->metrics.y_scale)); - unlockFace(); - return answer; - } - return calculatedCapHeight(); -} - -QFixed QFontEngineFT::descent() const -{ - QFixed v = QFixed::fromFixed(-metrics.descender); - if (scalableBitmapScaleFactor != 1) - v *= scalableBitmapScaleFactor; - return v; -} - -QFixed QFontEngineFT::leading() const -{ - QFixed v = QFixed::fromFixed(metrics.height - metrics.ascender + metrics.descender); - if (scalableBitmapScaleFactor != 1) - v *= scalableBitmapScaleFactor; - return v; -} - -QFixed QFontEngineFT::xHeight() const -{ - TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2); - if (os2 && os2->sxHeight) { - lockFace(); - QFixed answer = QFixed(os2->sxHeight * freetype->face->size->metrics.y_ppem) / emSquareSize(); - unlockFace(); - return answer; - } - - return QFontEngine::xHeight(); -} - -QFixed QFontEngineFT::averageCharWidth() const -{ - TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2); - if (os2 && os2->xAvgCharWidth) { - lockFace(); - QFixed answer = QFixed(os2->xAvgCharWidth * freetype->face->size->metrics.x_ppem) / emSquareSize(); - unlockFace(); - return answer; - } - - return QFontEngine::averageCharWidth(); -} - -qreal QFontEngineFT::maxCharWidth() const -{ - QFixed max_advance = QFixed::fromFixed(metrics.max_advance); - if (scalableBitmapScaleFactor != 1) - max_advance *= scalableBitmapScaleFactor; - return max_advance.toReal(); -} - -QFixed QFontEngineFT::lineThickness() const -{ - return line_thickness; -} - -QFixed QFontEngineFT::underlinePosition() const -{ - return underline_position; -} - -void QFontEngineFT::doKerning(QGlyphLayout *g, QFontEngine::ShaperFlags flags) const -{ - if (!kerning_pairs_loaded) { - kerning_pairs_loaded = true; - lockFace(); - if (freetype->face->size->metrics.x_ppem != 0) { - QFixed scalingFactor = emSquareSize() / QFixed(freetype->face->size->metrics.x_ppem); - unlockFace(); - const_cast(this)->loadKerningPairs(scalingFactor); - } else { - unlockFace(); - } - } - - if (shouldUseDesignMetrics(flags) && !(fontDef.styleStrategy & QFont::ForceIntegerMetrics)) - flags |= DesignMetrics; - else - flags &= ~DesignMetrics; - - QFontEngine::doKerning(g, flags); -} - -static inline FT_Matrix QTransformToFTMatrix(const QTransform &matrix) -{ - FT_Matrix m; - - m.xx = FT_Fixed(matrix.m11() * 65536); - m.xy = FT_Fixed(-matrix.m21() * 65536); - m.yx = FT_Fixed(-matrix.m12() * 65536); - m.yy = FT_Fixed(matrix.m22() * 65536); - - return m; -} - -QFontEngineFT::QGlyphSet *QFontEngineFT::loadGlyphSet(const QTransform &matrix) -{ - if (matrix.type() > QTransform::TxShear || !cacheEnabled) - return 0; - - // FT_Set_Transform only supports scalable fonts - if (!FT_IS_SCALABLE(freetype->face)) - return matrix.type() <= QTransform::TxTranslate ? &defaultGlyphSet : Q_NULLPTR; - - FT_Matrix m = QTransformToFTMatrix(matrix); - - QGlyphSet *gs = 0; - - for (int i = 0; i < transformedGlyphSets.count(); ++i) { - const QGlyphSet &g = transformedGlyphSets.at(i); - if (g.transformationMatrix.xx == m.xx - && g.transformationMatrix.xy == m.xy - && g.transformationMatrix.yx == m.yx - && g.transformationMatrix.yy == m.yy) { - - // found a match, move it to the front - transformedGlyphSets.move(i, 0); - gs = &transformedGlyphSets[0]; - break; - } - } - - if (!gs) { - // don't cache more than 10 transformations - if (transformedGlyphSets.count() >= 10) { - transformedGlyphSets.move(transformedGlyphSets.size() - 1, 0); - } else { - transformedGlyphSets.prepend(QGlyphSet()); - } - gs = &transformedGlyphSets[0]; - gs->clear(); - gs->transformationMatrix = m; - gs->outline_drawing = fontDef.pixelSize * fontDef.pixelSize * qAbs(matrix.det()) >= QT_MAX_CACHED_GLYPH_SIZE * QT_MAX_CACHED_GLYPH_SIZE; - } - Q_ASSERT(gs != 0); - - return gs; -} - -void QFontEngineFT::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics) -{ - FT_Face face = lockFace(Unscaled); - FT_Set_Transform(face, 0, 0); - FT_Load_Glyph(face, glyph, FT_LOAD_NO_BITMAP); - - int left = face->glyph->metrics.horiBearingX; - int right = face->glyph->metrics.horiBearingX + face->glyph->metrics.width; - int top = face->glyph->metrics.horiBearingY; - int bottom = face->glyph->metrics.horiBearingY - face->glyph->metrics.height; - - QFixedPoint p; - p.x = 0; - p.y = 0; - - metrics->width = QFixed::fromFixed(right-left); - metrics->height = QFixed::fromFixed(top-bottom); - metrics->x = QFixed::fromFixed(left); - metrics->y = QFixed::fromFixed(-top); - metrics->xoff = QFixed::fromFixed(face->glyph->advance.x); - - if (!FT_IS_SCALABLE(freetype->face)) - QFreetypeFace::addBitmapToPath(face->glyph, p, path); - else - QFreetypeFace::addGlyphToPath(face, face->glyph, p, path, face->units_per_EM << 6, face->units_per_EM << 6); - - FT_Set_Transform(face, &freetype->matrix, 0); - unlockFace(); -} - -bool QFontEngineFT::supportsTransformation(const QTransform &transform) const -{ - return transform.type() <= QTransform::TxRotate; -} - -void QFontEngineFT::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags) -{ - if (!glyphs.numGlyphs) - return; - - if (FT_IS_SCALABLE(freetype->face)) { - QFontEngine::addOutlineToPath(x, y, glyphs, path, flags); - } else { - QVarLengthArray positions; - QVarLengthArray positioned_glyphs; - QTransform matrix; - matrix.translate(x, y); - getGlyphPositions(glyphs, matrix, flags, positioned_glyphs, positions); - - FT_Face face = lockFace(Unscaled); - for (int gl = 0; gl < glyphs.numGlyphs; gl++) { - FT_UInt glyph = positioned_glyphs[gl]; - FT_Load_Glyph(face, glyph, FT_LOAD_TARGET_MONO); - QFreetypeFace::addBitmapToPath(face->glyph, positions[gl], path); - } - unlockFace(); - } -} - -void QFontEngineFT::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int numGlyphs, - QPainterPath *path, QTextItem::RenderFlags) -{ - FT_Face face = lockFace(Unscaled); - - for (int gl = 0; gl < numGlyphs; gl++) { - FT_UInt glyph = glyphs[gl]; - - FT_Load_Glyph(face, glyph, FT_LOAD_NO_BITMAP); - - FT_GlyphSlot g = face->glyph; - if (g->format != FT_GLYPH_FORMAT_OUTLINE) - continue; - if (embolden) - FT_GlyphSlot_Embolden(g); - if (obliquen) - FT_GlyphSlot_Oblique(g); - QFreetypeFace::addGlyphToPath(face, g, positions[gl], path, xsize, ysize); - } - unlockFace(); -} - -glyph_t QFontEngineFT::glyphIndex(uint ucs4) const -{ - glyph_t glyph = ucs4 < QFreetypeFace::cmapCacheSize ? freetype->cmapCache[ucs4] : 0; - if (glyph == 0) { - FT_Face face = freetype->face; - glyph = FT_Get_Char_Index(face, ucs4); - if (glyph == 0) { - // Certain fonts don't have no-break space and tab, - // while we usually want to render them as space - if (ucs4 == QChar::Nbsp || ucs4 == QChar::Tabulation) { - glyph = FT_Get_Char_Index(face, QChar::Space); - } else if (freetype->symbol_map) { - // Symbol fonts can have more than one CMAPs, FreeType should take the - // correct one for us by default, so we always try FT_Get_Char_Index - // first. If it didn't work (returns 0), we will explicitly set the - // CMAP to symbol font one and try again. symbol_map is not always the - // correct one because in certain fonts like Wingdings symbol_map only - // contains PUA codepoints instead of the common ones. - FT_Set_Charmap(face, freetype->symbol_map); - glyph = FT_Get_Char_Index(face, ucs4); - FT_Set_Charmap(face, freetype->unicode_map); - } - } - if (ucs4 < QFreetypeFace::cmapCacheSize) - freetype->cmapCache[ucs4] = glyph; - } - - return glyph; -} - -bool QFontEngineFT::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, - QFontEngine::ShaperFlags flags) const -{ - Q_ASSERT(glyphs->numGlyphs >= *nglyphs); - if (*nglyphs < len) { - *nglyphs = len; - return false; - } - - int glyph_pos = 0; - if (freetype->symbol_map) { - FT_Face face = freetype->face; - QStringIterator it(str, str + len); - while (it.hasNext()) { - uint uc = it.next(); - glyphs->glyphs[glyph_pos] = uc < QFreetypeFace::cmapCacheSize ? freetype->cmapCache[uc] : 0; - if ( !glyphs->glyphs[glyph_pos] ) { - // Symbol fonts can have more than one CMAPs, FreeType should take the - // correct one for us by default, so we always try FT_Get_Char_Index - // first. If it didn't work (returns 0), we will explicitly set the - // CMAP to symbol font one and try again. symbol_map is not always the - // correct one because in certain fonts like Wingdings symbol_map only - // contains PUA codepoints instead of the common ones. - glyph_t glyph = FT_Get_Char_Index(face, uc); - // Certain symbol fonts don't have no-break space (0xa0) and tab (0x9), - // while we usually want to render them as space - if (!glyph && (uc == 0xa0 || uc == 0x9)) { - uc = 0x20; - glyph = FT_Get_Char_Index(face, uc); - } - if (!glyph) { - FT_Set_Charmap(face, freetype->symbol_map); - glyph = FT_Get_Char_Index(face, uc); - FT_Set_Charmap(face, freetype->unicode_map); - } - glyphs->glyphs[glyph_pos] = glyph; - if (uc < QFreetypeFace::cmapCacheSize) - freetype->cmapCache[uc] = glyph; - } - ++glyph_pos; - } - } else { - FT_Face face = freetype->face; - QStringIterator it(str, str + len); - while (it.hasNext()) { - uint uc = it.next(); - glyphs->glyphs[glyph_pos] = uc < QFreetypeFace::cmapCacheSize ? freetype->cmapCache[uc] : 0; - if (!glyphs->glyphs[glyph_pos]) { - { - redo: - glyph_t glyph = FT_Get_Char_Index(face, uc); - if (!glyph && (uc == 0xa0 || uc == 0x9)) { - uc = 0x20; - goto redo; - } - glyphs->glyphs[glyph_pos] = glyph; - if (uc < QFreetypeFace::cmapCacheSize) - freetype->cmapCache[uc] = glyph; - } - } - ++glyph_pos; - } - } - - *nglyphs = glyph_pos; - glyphs->numGlyphs = glyph_pos; - - if (!(flags & GlyphIndicesOnly)) - recalcAdvances(glyphs, flags); - - return true; -} - -bool QFontEngineFT::shouldUseDesignMetrics(QFontEngine::ShaperFlags flags) const -{ - if (!FT_IS_SCALABLE(freetype->face)) - return false; - - return default_hint_style == HintNone || default_hint_style == HintLight || (flags & DesignMetrics); -} - -QFixed QFontEngineFT::scaledBitmapMetrics(QFixed m) const -{ - return m * scalableBitmapScaleFactor; -} - -glyph_metrics_t QFontEngineFT::scaledBitmapMetrics(const glyph_metrics_t &m) const -{ - glyph_metrics_t metrics; - metrics.x = scaledBitmapMetrics(m.x); - metrics.y = scaledBitmapMetrics(m.y); - metrics.width = scaledBitmapMetrics(m.width); - metrics.height = scaledBitmapMetrics(m.height); - metrics.xoff = scaledBitmapMetrics(m.xoff); - metrics.yoff = scaledBitmapMetrics(m.yoff); - return metrics; -} - -void QFontEngineFT::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const -{ - FT_Face face = 0; - bool design = shouldUseDesignMetrics(flags); - for (int i = 0; i < glyphs->numGlyphs; i++) { - Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(glyphs->glyphs[i]) : 0; - // Since we are passing Format_None to loadGlyph, use same default format logic as loadGlyph - GlyphFormat acceptableFormat = (defaultFormat != Format_None) ? defaultFormat : Format_Mono; - if (g && g->format == acceptableFormat) { - glyphs->advances[i] = design ? QFixed::fromFixed(g->linearAdvance) : QFixed(g->advance); - } else { - if (!face) - face = lockFace(); - g = loadGlyph(cacheEnabled ? &defaultGlyphSet : 0, glyphs->glyphs[i], 0, Format_None, true); - if (g) - glyphs->advances[i] = design ? QFixed::fromFixed(g->linearAdvance) : QFixed(g->advance); - else - glyphs->advances[i] = design ? QFixed::fromFixed(face->glyph->linearHoriAdvance >> 10) - : QFixed::fromFixed(face->glyph->metrics.horiAdvance).round(); - if (!cacheEnabled && g != &emptyGlyph) - delete g; - } - - if (scalableBitmapScaleFactor != 1) - glyphs->advances[i] *= scalableBitmapScaleFactor; - } - if (face) - unlockFace(); - - if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { - for (int i = 0; i < glyphs->numGlyphs; ++i) - glyphs->advances[i] = glyphs->advances[i].round(); - } -} - -glyph_metrics_t QFontEngineFT::boundingBox(const QGlyphLayout &glyphs) -{ - FT_Face face = 0; - - glyph_metrics_t overall; - // initialize with line height, we get the same behaviour on all platforms - if (!isScalableBitmap()) { - overall.y = -ascent(); - overall.height = ascent() + descent(); - } else { - overall.y = QFixed::fromFixed(-metrics.ascender); - overall.height = QFixed::fromFixed(metrics.ascender - metrics.descender); - } - - QFixed ymax = 0; - QFixed xmax = 0; - for (int i = 0; i < glyphs.numGlyphs; i++) { - Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(glyphs.glyphs[i]) : 0; - if (!g) { - if (!face) - face = lockFace(); - g = loadGlyph(cacheEnabled ? &defaultGlyphSet : 0, glyphs.glyphs[i], 0, Format_None, true); - } - if (g) { - QFixed x = overall.xoff + glyphs.offsets[i].x + g->x; - QFixed y = overall.yoff + glyphs.offsets[i].y - g->y; - overall.x = qMin(overall.x, x); - overall.y = qMin(overall.y, y); - xmax = qMax(xmax, x + g->width); - ymax = qMax(ymax, y + g->height); - overall.xoff += g->advance; - if (!cacheEnabled && g != &emptyGlyph) - delete g; - } else { - int left = FLOOR(face->glyph->metrics.horiBearingX); - int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width); - int top = CEIL(face->glyph->metrics.horiBearingY); - int bottom = FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height); - - QFixed x = overall.xoff + glyphs.offsets[i].x - (-TRUNC(left)); - QFixed y = overall.yoff + glyphs.offsets[i].y - TRUNC(top); - overall.x = qMin(overall.x, x); - overall.y = qMin(overall.y, y); - xmax = qMax(xmax, x + TRUNC(right - left)); - ymax = qMax(ymax, y + TRUNC(top - bottom)); - overall.xoff += int(TRUNC(ROUND(face->glyph->advance.x))); - } - } - overall.height = qMax(overall.height, ymax - overall.y); - overall.width = xmax - overall.x; - - if (face) - unlockFace(); - - if (isScalableBitmap()) - overall = scaledBitmapMetrics(overall); - return overall; -} - -glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph) -{ - FT_Face face = 0; - glyph_metrics_t overall; - Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(glyph) : 0; - if (!g) { - face = lockFace(); - g = loadGlyph(cacheEnabled ? &defaultGlyphSet : 0, glyph, 0, Format_None, true); - } - if (g) { - overall.x = g->x; - overall.y = -g->y; - overall.width = g->width; - overall.height = g->height; - overall.xoff = g->advance; - if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) - overall.xoff = overall.xoff.round(); - if (!cacheEnabled && g != &emptyGlyph) - delete g; - } else { - int left = FLOOR(face->glyph->metrics.horiBearingX); - int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width); - int top = CEIL(face->glyph->metrics.horiBearingY); - int bottom = FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height); - - overall.width = TRUNC(right-left); - overall.height = TRUNC(top-bottom); - overall.x = TRUNC(left); - overall.y = -TRUNC(top); - overall.xoff = TRUNC(ROUND(face->glyph->advance.x)); - } - if (face) - unlockFace(); - - if (isScalableBitmap()) - overall = scaledBitmapMetrics(overall); - return overall; -} - -glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph, const QTransform &matrix) -{ - return alphaMapBoundingBox(glyph, 0, matrix, QFontEngine::Format_None); -} - -glyph_metrics_t QFontEngineFT::alphaMapBoundingBox(glyph_t glyph, QFixed subPixelPosition, const QTransform &matrix, QFontEngine::GlyphFormat format) -{ - Glyph *g = loadGlyphFor(glyph, subPixelPosition, format, matrix, true); - - glyph_metrics_t overall; - if (g) { - overall.x = g->x; - overall.y = -g->y; - overall.width = g->width; - overall.height = g->height; - overall.xoff = g->advance; - if (!cacheEnabled && g != &emptyGlyph) - delete g; - } else { - FT_Face face = lockFace(); - int left = FLOOR(face->glyph->metrics.horiBearingX); - int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width); - int top = CEIL(face->glyph->metrics.horiBearingY); - int bottom = FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height); - - overall.width = TRUNC(right-left); - overall.height = TRUNC(top-bottom); - overall.x = TRUNC(left); - overall.y = -TRUNC(top); - overall.xoff = TRUNC(ROUND(face->glyph->advance.x)); - unlockFace(); - } - - if (isScalableBitmap()) - overall = scaledBitmapMetrics(overall); - return overall; -} - -static inline QImage alphaMapFromGlyphData(QFontEngineFT::Glyph *glyph, QFontEngine::GlyphFormat glyphFormat) -{ - if (glyph == Q_NULLPTR || glyph->height == 0 || glyph->width == 0) - return QImage(); - - QImage::Format format = QImage::Format_Invalid; - int bytesPerLine = -1; - switch (glyphFormat) { - case QFontEngine::Format_Mono: - format = QImage::Format_Mono; - bytesPerLine = ((glyph->width + 31) & ~31) >> 3; - break; - case QFontEngine::Format_A8: - format = QImage::Format_Alpha8; - bytesPerLine = (glyph->width + 3) & ~3; - break; - case QFontEngine::Format_A32: - format = QImage::Format_RGB32; - bytesPerLine = glyph->width * 4; - break; - default: - Q_UNREACHABLE(); - }; - - QImage img(static_cast(glyph->data), glyph->width, glyph->height, bytesPerLine, format); - if (format == QImage::Format_Mono) - img.setColor(1, QColor(Qt::white).rgba()); // Expands color table to 2 items; item 0 set to transparent. - return img; -} - -QImage *QFontEngineFT::lockedAlphaMapForGlyph(glyph_t glyphIndex, QFixed subPixelPosition, - QFontEngine::GlyphFormat neededFormat, - const QTransform &t, QPoint *offset) -{ - Q_ASSERT(currentlyLockedAlphaMap.isNull()); - - if (isBitmapFont()) - neededFormat = Format_Mono; - else if (neededFormat == Format_None && defaultFormat != Format_None) - neededFormat = defaultFormat; - else if (neededFormat == Format_None) - neededFormat = Format_A8; - - Glyph *glyph = loadGlyphFor(glyphIndex, subPixelPosition, neededFormat, t); - - if (offset != 0 && glyph != 0) - *offset = QPoint(glyph->x, -glyph->y); - - currentlyLockedAlphaMap = alphaMapFromGlyphData(glyph, neededFormat); - - const bool glyphHasGeometry = glyph != Q_NULLPTR && glyph->height != 0 && glyph->width != 0; - if (!cacheEnabled && glyph != &emptyGlyph) { - currentlyLockedAlphaMap = currentlyLockedAlphaMap.copy(); - delete glyph; - } - - if (!glyphHasGeometry) - return Q_NULLPTR; - - if (currentlyLockedAlphaMap.isNull()) - return QFontEngine::lockedAlphaMapForGlyph(glyphIndex, subPixelPosition, neededFormat, t, offset); - - QImageData *data = currentlyLockedAlphaMap.data_ptr(); - data->is_locked = true; - - return ¤tlyLockedAlphaMap; -} - -void QFontEngineFT::unlockAlphaMapForGlyph() -{ - QFontEngine::unlockAlphaMapForGlyph(); -} - -static inline bool is2dRotation(const QTransform &t) -{ - return qFuzzyCompare(t.m11(), t.m22()) && qFuzzyCompare(t.m12(), -t.m21()) - && qFuzzyCompare(t.m11()*t.m22() - t.m12()*t.m21(), qreal(1.0)); -} - -QFontEngineFT::Glyph *QFontEngineFT::loadGlyphFor(glyph_t g, - QFixed subPixelPosition, - GlyphFormat format, - const QTransform &t, - bool fetchBoundingBox, - bool disableOutlineDrawing) -{ - QGlyphSet *glyphSet = loadGlyphSet(t); - if (glyphSet != 0 && glyphSet->outline_drawing && !disableOutlineDrawing && !fetchBoundingBox) - return 0; - - Glyph *glyph = glyphSet != 0 ? glyphSet->getGlyph(g, subPixelPosition) : 0; - if (!glyph || glyph->format != format || (!fetchBoundingBox && !glyph->data)) { - QScopedValueRollback saved_default_hint_style(default_hint_style); - if (t.type() >= QTransform::TxScale && !is2dRotation(t)) - default_hint_style = HintNone; // disable hinting if the glyphs are transformed - - lockFace(); - FT_Matrix m = this->matrix; - FT_Matrix ftMatrix = glyphSet != 0 ? glyphSet->transformationMatrix : QTransformToFTMatrix(t); - FT_Matrix_Multiply(&ftMatrix, &m); - freetype->matrix = m; - glyph = loadGlyph(glyphSet, g, subPixelPosition, format, false, disableOutlineDrawing); - unlockFace(); - } - - return glyph; -} - -QImage QFontEngineFT::alphaMapForGlyph(glyph_t g, QFixed subPixelPosition) -{ - return alphaMapForGlyph(g, subPixelPosition, QTransform()); -} - -QImage QFontEngineFT::alphaMapForGlyph(glyph_t g, QFixed subPixelPosition, const QTransform &t) -{ - const GlyphFormat neededFormat = antialias ? Format_A8 : Format_Mono; - - Glyph *glyph = loadGlyphFor(g, subPixelPosition, neededFormat, t, false, true); - - QImage img = alphaMapFromGlyphData(glyph, neededFormat); - img = img.copy(); - - if (!cacheEnabled && glyph != &emptyGlyph) - delete glyph; - - if (!img.isNull()) - return img; - - return QFontEngine::alphaMapForGlyph(g, subPixelPosition, t); -} - -QImage QFontEngineFT::alphaRGBMapForGlyph(glyph_t g, QFixed subPixelPosition, const QTransform &t) -{ - if (t.type() > QTransform::TxRotate) - return QFontEngine::alphaRGBMapForGlyph(g, subPixelPosition, t); - - const GlyphFormat neededFormat = Format_A32; - - Glyph *glyph = loadGlyphFor(g, subPixelPosition, neededFormat, t, false, true); - - QImage img = alphaMapFromGlyphData(glyph, neededFormat); - img = img.copy(); - - if (!cacheEnabled && glyph != &emptyGlyph) - delete glyph; - - if (!img.isNull()) - return img; - - return QFontEngine::alphaRGBMapForGlyph(g, subPixelPosition, t); -} - -QImage QFontEngineFT::bitmapForGlyph(glyph_t g, QFixed subPixelPosition, const QTransform &t) -{ - Glyph *glyph = loadGlyphFor(g, subPixelPosition, defaultFormat, t); - if (glyph == Q_NULLPTR) - return QImage(); - - QImage img; - if (defaultFormat == GlyphFormat::Format_ARGB) - img = QImage(glyph->data, glyph->width, glyph->height, QImage::Format_ARGB32_Premultiplied).copy(); - else if (defaultFormat == GlyphFormat::Format_Mono) - img = QImage(glyph->data, glyph->width, glyph->height, QImage::Format_Mono).copy(); - - if (!img.isNull() && (!t.isIdentity() || scalableBitmapScaleFactor != 1)) { - QTransform trans(t); - const qreal scaleFactor = scalableBitmapScaleFactor.toReal(); - trans.scale(scaleFactor, scaleFactor); - img = img.transformed(trans, Qt::SmoothTransformation); - } - - if (!cacheEnabled && glyph != &emptyGlyph) - delete glyph; - - return img; -} - -void QFontEngineFT::removeGlyphFromCache(glyph_t glyph) -{ - defaultGlyphSet.removeGlyphFromCache(glyph, 0); -} - -int QFontEngineFT::glyphCount() const -{ - int count = 0; - FT_Face face = lockFace(); - if (face) { - count = face->num_glyphs; - unlockFace(); - } - return count; -} - -FT_Face QFontEngineFT::lockFace(Scaling scale) const -{ - freetype->lock(); - FT_Face face = freetype->face; - if (scale == Unscaled) { - if (FT_Set_Char_Size(face, face->units_per_EM << 6, face->units_per_EM << 6, 0, 0) == 0) { - freetype->xsize = face->units_per_EM << 6; - freetype->ysize = face->units_per_EM << 6; - } - } else if (freetype->xsize != xsize || freetype->ysize != ysize) { - FT_Set_Char_Size(face, xsize, ysize, 0, 0); - freetype->xsize = xsize; - freetype->ysize = ysize; - } - if (freetype->matrix.xx != matrix.xx || - freetype->matrix.yy != matrix.yy || - freetype->matrix.xy != matrix.xy || - freetype->matrix.yx != matrix.yx) { - freetype->matrix = matrix; - FT_Set_Transform(face, &freetype->matrix, 0); - } - - return face; -} - -void QFontEngineFT::unlockFace() const -{ - freetype->unlock(); -} - -FT_Face QFontEngineFT::non_locked_face() const -{ - return freetype->face; -} - - -QFontEngineFT::QGlyphSet::QGlyphSet() - : outline_drawing(false) -{ - transformationMatrix.xx = 0x10000; - transformationMatrix.yy = 0x10000; - transformationMatrix.xy = 0; - transformationMatrix.yx = 0; - memset(fast_glyph_data, 0, sizeof(fast_glyph_data)); - fast_glyph_count = 0; -} - -QFontEngineFT::QGlyphSet::~QGlyphSet() -{ - clear(); -} - -void QFontEngineFT::QGlyphSet::clear() -{ - if (fast_glyph_count > 0) { - for (int i = 0; i < 256; ++i) { - if (fast_glyph_data[i]) { - delete fast_glyph_data[i]; - fast_glyph_data[i] = 0; - } - } - fast_glyph_count = 0; - } - qDeleteAll(glyph_data); - glyph_data.clear(); -} - -void QFontEngineFT::QGlyphSet::removeGlyphFromCache(glyph_t index, QFixed subPixelPosition) -{ - if (useFastGlyphData(index, subPixelPosition)) { - if (fast_glyph_data[index]) { - delete fast_glyph_data[index]; - fast_glyph_data[index] = 0; - if (fast_glyph_count > 0) - --fast_glyph_count; - } - } else { - delete glyph_data.take(GlyphAndSubPixelPosition(index, subPixelPosition)); - } -} - -void QFontEngineFT::QGlyphSet::setGlyph(glyph_t index, QFixed subPixelPosition, Glyph *glyph) -{ - if (useFastGlyphData(index, subPixelPosition)) { - if (!fast_glyph_data[index]) - ++fast_glyph_count; - fast_glyph_data[index] = glyph; - } else { - glyph_data.insert(GlyphAndSubPixelPosition(index, subPixelPosition), glyph); - } -} - -int QFontEngineFT::getPointInOutline(glyph_t glyph, int flags, quint32 point, QFixed *xpos, QFixed *ypos, quint32 *nPoints) -{ - lockFace(); - bool hsubpixel = true; - int vfactor = 1; - int load_flags = loadFlags(0, Format_A8, flags, hsubpixel, vfactor); - int result = freetype->getPointInOutline(glyph, load_flags, point, xpos, ypos, nPoints); - unlockFace(); - return result; -} - -bool QFontEngineFT::initFromFontEngine(const QFontEngineFT *fe) -{ - if (!init(fe->faceId(), fe->antialias, fe->defaultFormat, fe->freetype)) - return false; - - // Increase the reference of this QFreetypeFace since one more QFontEngineFT - // will be using it - freetype->ref.ref(); - - default_load_flags = fe->default_load_flags; - default_hint_style = fe->default_hint_style; - antialias = fe->antialias; - transform = fe->transform; - embolden = fe->embolden; - obliquen = fe->obliquen; - subpixelType = fe->subpixelType; - lcdFilterType = fe->lcdFilterType; - embeddedbitmap = fe->embeddedbitmap; - - return true; -} - -QFontEngine *QFontEngineFT::cloneWithSize(qreal pixelSize) const -{ - QFontDef fontDef(this->fontDef); - fontDef.pixelSize = pixelSize; - QFontEngineFT *fe = new QFontEngineFT(fontDef); - if (!fe->initFromFontEngine(this)) { - delete fe; - return 0; - } else { - return fe; - } -} - -Qt::HANDLE QFontEngineFT::handle() const -{ - return non_locked_face(); -} - -QT_END_NAMESPACE - -#endif // QT_NO_FREETYPE diff --git a/src/gui/text/qfontengine_ft_p.h b/src/gui/text/qfontengine_ft_p.h deleted file mode 100644 index 32357d0076..0000000000 --- a/src/gui/text/qfontengine_ft_p.h +++ /dev/null @@ -1,364 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QFONTENGINE_FT_P_H -#define QFONTENGINE_FT_P_H -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "private/qfontengine_p.h" - -#ifndef QT_NO_FREETYPE - -#include -#include FT_FREETYPE_H - - -#ifndef Q_OS_WIN -#include -#endif - -#include - -QT_BEGIN_NAMESPACE - -class QFontEngineFTRawFont; -class QFontconfigDatabase; - -/* - * This class represents one font file on disk (like Arial.ttf) and is shared between all the font engines - * that show this font file (at different pixel sizes). - */ -class QFreetypeFace -{ -public: - void computeSize(const QFontDef &fontDef, int *xsize, int *ysize, bool *outline_drawing, QFixed *scalableBitmapScaleFactor); - QFontEngine::Properties properties() const; - bool getSfntTable(uint tag, uchar *buffer, uint *length) const; - - static QFreetypeFace *getFace(const QFontEngine::FaceId &face_id, - const QByteArray &fontData = QByteArray()); - void release(const QFontEngine::FaceId &face_id); - - // locks the struct for usage. Any read/write operations require locking. - void lock() - { - _lock.lock(); - } - void unlock() - { - _lock.unlock(); - } - - FT_Face face; - int xsize; // 26.6 - int ysize; // 26.6 - FT_Matrix matrix; - FT_CharMap unicode_map; - FT_CharMap symbol_map; - - enum { cmapCacheSize = 0x200 }; - glyph_t cmapCache[cmapCacheSize]; - - int fsType() const; - - int getPointInOutline(glyph_t glyph, int flags, quint32 point, QFixed *xpos, QFixed *ypos, quint32 *nPoints); - - bool isScalableBitmap() const; - - static void addGlyphToPath(FT_Face face, FT_GlyphSlot g, const QFixedPoint &point, QPainterPath *path, FT_Fixed x_scale, FT_Fixed y_scale); - static void addBitmapToPath(FT_GlyphSlot slot, const QFixedPoint &point, QPainterPath *path); - -private: - friend class QFontEngineFT; - friend class QtFreetypeData; - friend struct QScopedPointerDeleter; - QFreetypeFace() : _lock(QMutex::Recursive) {} - ~QFreetypeFace() {} - void cleanup(); - QAtomicInt ref; - QMutex _lock; - QByteArray fontData; - - QFontEngine::Holder hbFace; -}; - -// If this is exported this breaks compilation of the windows -// plugin as qfontengine_ft_p.h/.cpp are also compiled there -#ifndef Q_OS_WIN -class Q_GUI_EXPORT QFontEngineFT : public QFontEngine -#else -class QFontEngineFT : public QFontEngine -#endif -{ -public: - - /* we don't cache glyphs that are too large anyway, so we can make this struct rather small */ - struct Glyph { - ~Glyph(); - short linearAdvance; - unsigned char width; - unsigned char height; - short x; - short y; - short advance; - signed char format; - uchar *data; - }; - - struct GlyphInfo { - int linearAdvance; - unsigned short width; - unsigned short height; - short x; - short y; - short xOff; - short yOff; - }; - - struct GlyphAndSubPixelPosition - { - GlyphAndSubPixelPosition(glyph_t g, QFixed spp) : glyph(g), subPixelPosition(spp) {} - - bool operator==(const GlyphAndSubPixelPosition &other) const - { - return glyph == other.glyph && subPixelPosition == other.subPixelPosition; - } - - glyph_t glyph; - QFixed subPixelPosition; - }; - - struct QGlyphSet - { - QGlyphSet(); - ~QGlyphSet(); - FT_Matrix transformationMatrix; - bool outline_drawing; - - void removeGlyphFromCache(glyph_t index, QFixed subPixelPosition); - void clear(); - inline bool useFastGlyphData(glyph_t index, QFixed subPixelPosition) const { - return (index < 256 && subPixelPosition == 0); - } - inline Glyph *getGlyph(glyph_t index, QFixed subPixelPosition = 0) const; - void setGlyph(glyph_t index, QFixed spp, Glyph *glyph); - - inline bool isGlyphMissing(glyph_t index) const { return missing_glyphs.contains(index); } - inline void setGlyphMissing(glyph_t index) const { missing_glyphs.insert(index); } -private: - mutable QHash glyph_data; // maps from glyph index to glyph data - mutable QSet missing_glyphs; - mutable Glyph *fast_glyph_data[256]; // for fast lookup of glyphs < 256 - mutable int fast_glyph_count; - }; - - QFontEngine::FaceId faceId() const Q_DECL_OVERRIDE; - QFontEngine::Properties properties() const Q_DECL_OVERRIDE; - QFixed emSquareSize() const Q_DECL_OVERRIDE; - bool supportsSubPixelPositions() const Q_DECL_OVERRIDE - { - return default_hint_style == HintLight || - default_hint_style == HintNone; - } - - bool getSfntTableData(uint tag, uchar *buffer, uint *length) const Q_DECL_OVERRIDE; - int synthesized() const Q_DECL_OVERRIDE; - - QFixed ascent() const Q_DECL_OVERRIDE; - QFixed capHeight() const Q_DECL_OVERRIDE; - QFixed descent() const Q_DECL_OVERRIDE; - QFixed leading() const Q_DECL_OVERRIDE; - QFixed xHeight() const Q_DECL_OVERRIDE; - QFixed averageCharWidth() const Q_DECL_OVERRIDE; - - qreal maxCharWidth() const Q_DECL_OVERRIDE; - QFixed lineThickness() const Q_DECL_OVERRIDE; - QFixed underlinePosition() const Q_DECL_OVERRIDE; - - glyph_t glyphIndex(uint ucs4) const Q_DECL_OVERRIDE; - void doKerning(QGlyphLayout *, ShaperFlags) const Q_DECL_OVERRIDE; - - void getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics) Q_DECL_OVERRIDE; - - bool supportsTransformation(const QTransform &transform) const Q_DECL_OVERRIDE; - - void addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs, - QPainterPath *path, QTextItem::RenderFlags flags) Q_DECL_OVERRIDE; - void addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, - QPainterPath *path, QTextItem::RenderFlags flags) Q_DECL_OVERRIDE; - - bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const Q_DECL_OVERRIDE; - - glyph_metrics_t boundingBox(const QGlyphLayout &glyphs) Q_DECL_OVERRIDE; - glyph_metrics_t boundingBox(glyph_t glyph) Q_DECL_OVERRIDE; - glyph_metrics_t boundingBox(glyph_t glyph, const QTransform &matrix) Q_DECL_OVERRIDE; - - void recalcAdvances(QGlyphLayout *glyphs, ShaperFlags flags) const Q_DECL_OVERRIDE; - QImage alphaMapForGlyph(glyph_t g) Q_DECL_OVERRIDE { return alphaMapForGlyph(g, 0); } - QImage alphaMapForGlyph(glyph_t, QFixed) Q_DECL_OVERRIDE; - QImage alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition, const QTransform &t) Q_DECL_OVERRIDE; - QImage alphaRGBMapForGlyph(glyph_t, QFixed subPixelPosition, const QTransform &t) Q_DECL_OVERRIDE; - QImage bitmapForGlyph(glyph_t, QFixed subPixelPosition, const QTransform &t) Q_DECL_OVERRIDE; - glyph_metrics_t alphaMapBoundingBox(glyph_t glyph, - QFixed subPixelPosition, - const QTransform &matrix, - QFontEngine::GlyphFormat format) Q_DECL_OVERRIDE; - QImage *lockedAlphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition, - GlyphFormat neededFormat, const QTransform &t, - QPoint *offset) Q_DECL_OVERRIDE; - bool hasInternalCaching() const Q_DECL_OVERRIDE { return cacheEnabled; } - void unlockAlphaMapForGlyph() Q_DECL_OVERRIDE; - - void removeGlyphFromCache(glyph_t glyph) Q_DECL_OVERRIDE; - int glyphMargin(QFontEngine::GlyphFormat /* format */) Q_DECL_OVERRIDE { return 0; } - - int glyphCount() const Q_DECL_OVERRIDE; - - enum Scaling { - Scaled, - Unscaled - }; - FT_Face lockFace(Scaling scale = Scaled) const; - void unlockFace() const; - - FT_Face non_locked_face() const; - - inline bool drawAntialiased() const { return antialias; } - inline bool invalid() const { return xsize == 0 && ysize == 0; } - inline bool isBitmapFont() const { return defaultFormat == Format_Mono; } - inline bool isScalableBitmap() const { return freetype->isScalableBitmap(); } - - inline Glyph *loadGlyph(uint glyph, QFixed subPixelPosition, GlyphFormat format = Format_None, bool fetchMetricsOnly = false, bool disableOutlineDrawing = false) const - { return loadGlyph(cacheEnabled ? &defaultGlyphSet : 0, glyph, subPixelPosition, format, fetchMetricsOnly, disableOutlineDrawing); } - Glyph *loadGlyph(QGlyphSet *set, uint glyph, QFixed subPixelPosition, GlyphFormat = Format_None, bool fetchMetricsOnly = false, bool disableOutlineDrawing = false) const; - Glyph *loadGlyphFor(glyph_t g, QFixed subPixelPosition, GlyphFormat format, const QTransform &t, bool fetchBoundingBox = false, bool disableOutlineDrawing = false); - - QGlyphSet *loadGlyphSet(const QTransform &matrix); - - QFontEngineFT(const QFontDef &fd); - virtual ~QFontEngineFT(); - - bool init(FaceId faceId, bool antiaalias, GlyphFormat defaultFormat = Format_None, - const QByteArray &fontData = QByteArray()); - bool init(FaceId faceId, bool antialias, GlyphFormat format, - QFreetypeFace *freetypeFace); - - int getPointInOutline(glyph_t glyph, int flags, quint32 point, QFixed *xpos, QFixed *ypos, quint32 *nPoints) Q_DECL_OVERRIDE; - - void setQtDefaultHintStyle(QFont::HintingPreference hintingPreference); - void setDefaultHintStyle(HintStyle style) Q_DECL_OVERRIDE; - - QFontEngine *cloneWithSize(qreal pixelSize) const Q_DECL_OVERRIDE; - Qt::HANDLE handle() const Q_DECL_OVERRIDE; - bool initFromFontEngine(const QFontEngineFT *fontEngine); - - HintStyle defaultHintStyle() const { return default_hint_style; } -protected: - - QFreetypeFace *freetype; - mutable int default_load_flags; - HintStyle default_hint_style; - bool antialias; - bool transform; - bool embolden; - bool obliquen; - SubpixelAntialiasingType subpixelType; - int lcdFilterType; - bool embeddedbitmap; - bool cacheEnabled; - bool forceAutoHint; - -private: - friend class QFontEngineFTRawFont; - friend class QFontconfigDatabase; - friend class QBasicFontDatabase; - friend class QCoreTextFontDatabase; - friend class QFontEngineMultiFontConfig; - - int loadFlags(QGlyphSet *set, GlyphFormat format, int flags, bool &hsubpixel, int &vfactor) const; - bool shouldUseDesignMetrics(ShaperFlags flags) const; - QFixed scaledBitmapMetrics(QFixed m) const; - glyph_metrics_t scaledBitmapMetrics(const glyph_metrics_t &m) const; - - GlyphFormat defaultFormat; - FT_Matrix matrix; - - QList transformedGlyphSets; - mutable QGlyphSet defaultGlyphSet; - - QFontEngine::FaceId face_id; - - int xsize; - int ysize; - - QFixed line_thickness; - QFixed underline_position; - - FT_Size_Metrics metrics; - mutable bool kerning_pairs_loaded; - QFixed scalableBitmapScaleFactor; -}; - -inline uint qHash(const QFontEngineFT::GlyphAndSubPixelPosition &g) -{ - return (g.glyph << 8) | (g.subPixelPosition * 10).round().toInt(); -} - -inline QFontEngineFT::Glyph *QFontEngineFT::QGlyphSet::getGlyph(glyph_t index, QFixed subPixelPosition) const -{ - if (useFastGlyphData(index, subPixelPosition)) - return fast_glyph_data[index]; - return glyph_data.value(GlyphAndSubPixelPosition(index, subPixelPosition)); -} - -extern FT_Library qt_getFreetype(); - -QT_END_NAMESPACE - -#endif // QT_NO_FREETYPE - -#endif // QFONTENGINE_FT_P_H diff --git a/src/platformsupport/fontdatabases/basic/basic.pri b/src/platformsupport/fontdatabases/basic/basic.pri index c50dba3ce2..d16aabb4c7 100644 --- a/src/platformsupport/fontdatabases/basic/basic.pri +++ b/src/platformsupport/fontdatabases/basic/basic.pri @@ -1,9 +1,9 @@ HEADERS += \ $$PWD/qbasicfontdatabase_p.h \ - $$QT_SOURCE_TREE/src/gui/text/qfontengine_ft_p.h + $$PWD/qfontengine_ft_p.h SOURCES += \ $$PWD/qbasicfontdatabase.cpp \ - $$QT_SOURCE_TREE/src/gui/text/qfontengine_ft.cpp + $$PWD/qfontengine_ft.cpp QMAKE_USE += freetype diff --git a/src/platformsupport/fontdatabases/basic/qbasicfontdatabase.cpp b/src/platformsupport/fontdatabases/basic/qbasicfontdatabase.cpp index 60ddc9fa23..0826b0f2fb 100644 --- a/src/platformsupport/fontdatabases/basic/qbasicfontdatabase.cpp +++ b/src/platformsupport/fontdatabases/basic/qbasicfontdatabase.cpp @@ -49,8 +49,7 @@ #include #undef QT_NO_FREETYPE -#include -#include +#include #include #include FT_TRUETYPE_TABLES_H diff --git a/src/platformsupport/fontdatabases/basic/qfontengine_ft.cpp b/src/platformsupport/fontdatabases/basic/qfontengine_ft.cpp new file mode 100644 index 0000000000..de6da88245 --- /dev/null +++ b/src/platformsupport/fontdatabases/basic/qfontengine_ft.cpp @@ -0,0 +1,2190 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdir.h" +#include "qmetatype.h" +#include "qtextstream.h" +#include "qvariant.h" +#include "qfontengine_ft_p.h" +#include "private/qimage_p.h" +#include + +#ifndef QT_NO_FREETYPE + +#include "qfile.h" +#include "qfileinfo.h" +#include +#include "qthreadstorage.h" +#include +#include + +#include +#include FT_FREETYPE_H +#include FT_OUTLINE_H +#include FT_SYNTHESIS_H +#include FT_TRUETYPE_TABLES_H +#include FT_TYPE1_TABLES_H +#include FT_GLYPH_H + +#if defined(FT_LCD_FILTER_H) +#include FT_LCD_FILTER_H +#endif + +#if defined(FT_CONFIG_OPTIONS_H) +#include FT_CONFIG_OPTIONS_H +#endif + +#if defined(FT_LCD_FILTER_H) +#define QT_USE_FREETYPE_LCDFILTER +#endif + +#ifdef QT_LINUXBASE +#include FT_ERRORS_H +#endif + +#if !defined(QT_MAX_CACHED_GLYPH_SIZE) +# define QT_MAX_CACHED_GLYPH_SIZE 64 +#endif + +QT_BEGIN_NAMESPACE + +#define FLOOR(x) ((x) & -64) +#define CEIL(x) (((x)+63) & -64) +#define TRUNC(x) ((x) >> 6) +#define ROUND(x) (((x)+32) & -64) + +static bool ft_getSfntTable(void *user_data, uint tag, uchar *buffer, uint *length) +{ + FT_Face face = (FT_Face)user_data; + + bool result = false; + if (FT_IS_SFNT(face)) { + FT_ULong len = *length; + result = FT_Load_Sfnt_Table(face, tag, 0, buffer, &len) == FT_Err_Ok; + *length = len; + Q_ASSERT(!result || int(*length) > 0); + } + + return result; +} + +static QFontEngineFT::Glyph emptyGlyph = {0, 0, 0, 0, 0, 0, 0, 0}; + +static const QFontEngine::HintStyle ftInitialDefaultHintStyle = +#ifdef Q_OS_WIN + QFontEngineFT::HintFull; +#else + QFontEngineFT::HintNone; +#endif + +// -------------------------- Freetype support ------------------------------ + +class QtFreetypeData +{ +public: + QtFreetypeData() + : library(0) + { } + ~QtFreetypeData(); + + FT_Library library; + QHash faces; +}; + +QtFreetypeData::~QtFreetypeData() +{ + for (QHash::ConstIterator iter = faces.cbegin(); iter != faces.cend(); ++iter) + iter.value()->cleanup(); + faces.clear(); + FT_Done_FreeType(library); + library = 0; +} + +#ifdef QT_NO_THREAD +Q_GLOBAL_STATIC(QtFreetypeData, theFreetypeData) + +QtFreetypeData *qt_getFreetypeData() +{ + return theFreetypeData(); +} +#else +Q_GLOBAL_STATIC(QThreadStorage, theFreetypeData) + +QtFreetypeData *qt_getFreetypeData() +{ + QtFreetypeData *&freetypeData = theFreetypeData()->localData(); + if (!freetypeData) + freetypeData = new QtFreetypeData; + return freetypeData; +} +#endif + +FT_Library qt_getFreetype() +{ + QtFreetypeData *freetypeData = qt_getFreetypeData(); + if (!freetypeData->library) + FT_Init_FreeType(&freetypeData->library); + return freetypeData->library; +} + +int QFreetypeFace::fsType() const +{ + int fsType = 0; + TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(face, ft_sfnt_os2); + if (os2) + fsType = os2->fsType; + return fsType; +} + +int QFreetypeFace::getPointInOutline(glyph_t glyph, int flags, quint32 point, QFixed *xpos, QFixed *ypos, quint32 *nPoints) +{ + if (int error = FT_Load_Glyph(face, glyph, flags)) + return error; + + if (face->glyph->format != FT_GLYPH_FORMAT_OUTLINE) + return Err_Invalid_SubTable; + + *nPoints = face->glyph->outline.n_points; + if (!(*nPoints)) + return Err_Ok; + + if (point > *nPoints) + return Err_Invalid_SubTable; + + *xpos = QFixed::fromFixed(face->glyph->outline.points[point].x); + *ypos = QFixed::fromFixed(face->glyph->outline.points[point].y); + + return Err_Ok; +} + +bool QFreetypeFace::isScalableBitmap() const +{ +#ifdef FT_HAS_COLOR + return !FT_IS_SCALABLE(face) && FT_HAS_COLOR(face); +#else + return false; +#endif +} + +extern QByteArray qt_fontdata_from_index(int); + +/* + * One font file can contain more than one font (bold/italic for example) + * find the right one and return it. + * + * Returns the freetype face or 0 in case of an empty file or any other problems + * (like not being able to open the file) + */ +QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id, + const QByteArray &fontData) +{ + if (face_id.filename.isEmpty() && fontData.isEmpty()) + return 0; + + QtFreetypeData *freetypeData = qt_getFreetypeData(); + if (!freetypeData->library) + FT_Init_FreeType(&freetypeData->library); + + QFreetypeFace *freetype = freetypeData->faces.value(face_id, 0); + if (freetype) { + freetype->ref.ref(); + } else { + QScopedPointer newFreetype(new QFreetypeFace); + FT_Face face; + if (!face_id.filename.isEmpty()) { + QString fileName = QFile::decodeName(face_id.filename); + if (face_id.filename.startsWith(":qmemoryfonts/")) { + // from qfontdatabase.cpp + QByteArray idx = face_id.filename; + idx.remove(0, 14); // remove ':qmemoryfonts/' + bool ok = false; + newFreetype->fontData = qt_fontdata_from_index(idx.toInt(&ok)); + if (!ok) + newFreetype->fontData = QByteArray(); + } else if (!QFileInfo(fileName).isNativePath()) { + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly)) { + return 0; + } + newFreetype->fontData = file.readAll(); + } + } else { + newFreetype->fontData = fontData; + } + if (!newFreetype->fontData.isEmpty()) { + if (FT_New_Memory_Face(freetypeData->library, (const FT_Byte *)newFreetype->fontData.constData(), newFreetype->fontData.size(), face_id.index, &face)) { + return 0; + } + } else if (FT_New_Face(freetypeData->library, face_id.filename, face_id.index, &face)) { + return 0; + } + newFreetype->face = face; + + newFreetype->ref.store(1); + newFreetype->xsize = 0; + newFreetype->ysize = 0; + newFreetype->matrix.xx = 0x10000; + newFreetype->matrix.yy = 0x10000; + newFreetype->matrix.xy = 0; + newFreetype->matrix.yx = 0; + newFreetype->unicode_map = 0; + newFreetype->symbol_map = 0; + + memset(newFreetype->cmapCache, 0, sizeof(newFreetype->cmapCache)); + + for (int i = 0; i < newFreetype->face->num_charmaps; ++i) { + FT_CharMap cm = newFreetype->face->charmaps[i]; + switch(cm->encoding) { + case FT_ENCODING_UNICODE: + newFreetype->unicode_map = cm; + break; + case FT_ENCODING_APPLE_ROMAN: + case FT_ENCODING_ADOBE_LATIN_1: + if (!newFreetype->unicode_map || newFreetype->unicode_map->encoding != FT_ENCODING_UNICODE) + newFreetype->unicode_map = cm; + break; + case FT_ENCODING_ADOBE_CUSTOM: + case FT_ENCODING_MS_SYMBOL: + if (!newFreetype->symbol_map) + newFreetype->symbol_map = cm; + break; + default: + break; + } + } + + if (!FT_IS_SCALABLE(newFreetype->face) && newFreetype->face->num_fixed_sizes == 1) + FT_Set_Char_Size(face, newFreetype->face->available_sizes[0].x_ppem, newFreetype->face->available_sizes[0].y_ppem, 0, 0); + + FT_Set_Charmap(newFreetype->face, newFreetype->unicode_map); + QT_TRY { + freetypeData->faces.insert(face_id, newFreetype.data()); + } QT_CATCH(...) { + newFreetype.take()->release(face_id); + // we could return null in principle instead of throwing + QT_RETHROW; + } + freetype = newFreetype.take(); + } + return freetype; +} + +void QFreetypeFace::cleanup() +{ + hbFace.reset(); + FT_Done_Face(face); + face = 0; +} + +void QFreetypeFace::release(const QFontEngine::FaceId &face_id) +{ + if (!ref.deref()) { + if (face) { + QtFreetypeData *freetypeData = qt_getFreetypeData(); + + cleanup(); + + auto it = freetypeData->faces.constFind(face_id); + if (it != freetypeData->faces.constEnd()) + freetypeData->faces.erase(it); + + if (freetypeData->faces.isEmpty()) { + FT_Done_FreeType(freetypeData->library); + freetypeData->library = 0; + } + } + + delete this; + } +} + + +void QFreetypeFace::computeSize(const QFontDef &fontDef, int *xsize, int *ysize, bool *outline_drawing, QFixed *scalableBitmapScaleFactor) +{ + *ysize = qRound(fontDef.pixelSize * 64); + *xsize = *ysize * fontDef.stretch / 100; + *scalableBitmapScaleFactor = 1; + *outline_drawing = false; + + if (!(face->face_flags & FT_FACE_FLAG_SCALABLE)) { + int best = 0; + if (!isScalableBitmap()) { + /* + * Bitmap only faces must match exactly, so find the closest + * one (height dominant search) + */ + for (int i = 1; i < face->num_fixed_sizes; i++) { + if (qAbs(*ysize - face->available_sizes[i].y_ppem) < + qAbs(*ysize - face->available_sizes[best].y_ppem) || + (qAbs(*ysize - face->available_sizes[i].y_ppem) == + qAbs(*ysize - face->available_sizes[best].y_ppem) && + qAbs(*xsize - face->available_sizes[i].x_ppem) < + qAbs(*xsize - face->available_sizes[best].x_ppem))) { + best = i; + } + } + } else { + // Select the shortest bitmap strike whose height is larger than the desired height + for (int i = 1; i < face->num_fixed_sizes; i++) { + if (face->available_sizes[i].y_ppem < *ysize) { + if (face->available_sizes[i].y_ppem > face->available_sizes[best].y_ppem) + best = i; + } else if (face->available_sizes[best].y_ppem < *ysize) { + best = i; + } else if (face->available_sizes[i].y_ppem < face->available_sizes[best].y_ppem) { + best = i; + } + } + } + + // According to freetype documentation we must use FT_Select_Size + // to make sure we can select the desired bitmap strike index + if (FT_Select_Size(face, best) == 0) { + if (isScalableBitmap()) + *scalableBitmapScaleFactor = QFixed::fromReal((qreal)fontDef.pixelSize / face->available_sizes[best].height); + *xsize = face->available_sizes[best].x_ppem; + *ysize = face->available_sizes[best].y_ppem; + } else { + *xsize = *ysize = 0; + } + } else { + *outline_drawing = (*xsize > (QT_MAX_CACHED_GLYPH_SIZE<<6) || *ysize > (QT_MAX_CACHED_GLYPH_SIZE<<6)); + } +} + +QFontEngine::Properties QFreetypeFace::properties() const +{ + QFontEngine::Properties p; + p.postscriptName = FT_Get_Postscript_Name(face); + PS_FontInfoRec font_info; + if (FT_Get_PS_Font_Info(face, &font_info) == 0) + p.copyright = font_info.notice; + if (FT_IS_SCALABLE(face)) { + p.ascent = face->ascender; + p.descent = -face->descender; + p.leading = face->height - face->ascender + face->descender; + p.emSquare = face->units_per_EM; + p.boundingBox = QRectF(face->bbox.xMin, -face->bbox.yMax, + face->bbox.xMax - face->bbox.xMin, + face->bbox.yMax - face->bbox.yMin); + } else { + p.ascent = QFixed::fromFixed(face->size->metrics.ascender); + p.descent = QFixed::fromFixed(-face->size->metrics.descender); + p.leading = QFixed::fromFixed(face->size->metrics.height - face->size->metrics.ascender + face->size->metrics.descender); + p.emSquare = face->size->metrics.y_ppem; +// p.boundingBox = QRectF(-p.ascent.toReal(), 0, (p.ascent + p.descent).toReal(), face->size->metrics.max_advance/64.); + p.boundingBox = QRectF(0, -p.ascent.toReal(), + face->size->metrics.max_advance/64, (p.ascent + p.descent).toReal() ); + } + p.italicAngle = 0; + p.capHeight = p.ascent; + p.lineWidth = face->underline_thickness; + + return p; +} + +bool QFreetypeFace::getSfntTable(uint tag, uchar *buffer, uint *length) const +{ + return ft_getSfntTable(face, tag, buffer, length); +} + +/* Some fonts (such as MingLiu rely on hinting to scale different + components to their correct sizes. While this is really broken (it + should be done in the component glyph itself, not the hinter) we + will have to live with it. + + This means we can not use FT_LOAD_NO_HINTING to get the glyph + outline. All we can do is to load the unscaled glyph and scale it + down manually when required. +*/ +static void scaleOutline(FT_Face face, FT_GlyphSlot g, FT_Fixed x_scale, FT_Fixed y_scale) +{ + x_scale = FT_MulDiv(x_scale, 1 << 10, face->units_per_EM); + y_scale = FT_MulDiv(y_scale, 1 << 10, face->units_per_EM); + FT_Vector *p = g->outline.points; + const FT_Vector *e = p + g->outline.n_points; + while (p < e) { + p->x = FT_MulFix(p->x, x_scale); + p->y = FT_MulFix(p->y, y_scale); + ++p; + } +} + +#define GLYPH2PATH_DEBUG QT_NO_QDEBUG_MACRO // qDebug +void QFreetypeFace::addGlyphToPath(FT_Face face, FT_GlyphSlot g, const QFixedPoint &point, QPainterPath *path, FT_Fixed x_scale, FT_Fixed y_scale) +{ + const qreal factor = 1/64.; + scaleOutline(face, g, x_scale, y_scale); + + QPointF cp = point.toPointF(); + + // convert the outline to a painter path + int i = 0; + for (int j = 0; j < g->outline.n_contours; ++j) { + int last_point = g->outline.contours[j]; + GLYPH2PATH_DEBUG() << "contour:" << i << "to" << last_point; + QPointF start = QPointF(g->outline.points[i].x*factor, -g->outline.points[i].y*factor); + if (!(g->outline.tags[i] & 1)) { // start point is not on curve: + if (!(g->outline.tags[last_point] & 1)) { // end point is not on curve: + GLYPH2PATH_DEBUG() << " start and end point are not on curve"; + start = (QPointF(g->outline.points[last_point].x*factor, + -g->outline.points[last_point].y*factor) + start) / 2.0; + } else { + GLYPH2PATH_DEBUG() << " end point is on curve, start is not"; + start = QPointF(g->outline.points[last_point].x*factor, + -g->outline.points[last_point].y*factor); + } + --i; // to use original start point as control point below + } + start += cp; + GLYPH2PATH_DEBUG() << " start at" << start; + + path->moveTo(start); + QPointF c[4]; + c[0] = start; + int n = 1; + while (i < last_point) { + ++i; + c[n] = cp + QPointF(g->outline.points[i].x*factor, -g->outline.points[i].y*factor); + GLYPH2PATH_DEBUG() << " " << i << c[n] << "tag =" << (int)g->outline.tags[i] + << ": on curve =" << (bool)(g->outline.tags[i] & 1); + ++n; + switch (g->outline.tags[i] & 3) { + case 2: + // cubic bezier element + if (n < 4) + continue; + c[3] = (c[3] + c[2])/2; + --i; + break; + case 0: + // quadratic bezier element + if (n < 3) + continue; + c[3] = (c[1] + c[2])/2; + c[2] = (2*c[1] + c[3])/3; + c[1] = (2*c[1] + c[0])/3; + --i; + break; + case 1: + case 3: + if (n == 2) { + GLYPH2PATH_DEBUG() << " lineTo" << c[1]; + path->lineTo(c[1]); + c[0] = c[1]; + n = 1; + continue; + } else if (n == 3) { + c[3] = c[2]; + c[2] = (2*c[1] + c[3])/3; + c[1] = (2*c[1] + c[0])/3; + } + break; + } + GLYPH2PATH_DEBUG() << " cubicTo" << c[1] << c[2] << c[3]; + path->cubicTo(c[1], c[2], c[3]); + c[0] = c[3]; + n = 1; + } + + if (n == 1) { + GLYPH2PATH_DEBUG() << " closeSubpath"; + path->closeSubpath(); + } else { + c[3] = start; + if (n == 2) { + c[2] = (2*c[1] + c[3])/3; + c[1] = (2*c[1] + c[0])/3; + } + GLYPH2PATH_DEBUG() << " close cubicTo" << c[1] << c[2] << c[3]; + path->cubicTo(c[1], c[2], c[3]); + } + ++i; + } +} + +extern void qt_addBitmapToPath(qreal x0, qreal y0, const uchar *image_data, int bpl, int w, int h, QPainterPath *path); + +void QFreetypeFace::addBitmapToPath(FT_GlyphSlot slot, const QFixedPoint &point, QPainterPath *path) +{ + if (slot->format != FT_GLYPH_FORMAT_BITMAP + || slot->bitmap.pixel_mode != FT_PIXEL_MODE_MONO) + return; + + QPointF cp = point.toPointF(); + qt_addBitmapToPath(cp.x() + TRUNC(slot->metrics.horiBearingX), cp.y() - TRUNC(slot->metrics.horiBearingY), + slot->bitmap.buffer, slot->bitmap.pitch, slot->bitmap.width, slot->bitmap.rows, path); +} + +QFontEngineFT::Glyph::~Glyph() +{ + delete [] data; +} + +struct LcdFilterDummy +{ + static inline void filterPixel(uchar &, uchar &, uchar &) + {} +}; + +struct LcdFilterLegacy +{ + static inline void filterPixel(uchar &red, uchar &green, uchar &blue) + { + uint r = red, g = green, b = blue; + // intra-pixel filter used by the legacy filter (adopted from _ft_lcd_filter_legacy) + red = (r * uint(65538 * 9/13) + g * uint(65538 * 1/6) + b * uint(65538 * 1/13)) / 65536; + green = (r * uint(65538 * 3/13) + g * uint(65538 * 4/6) + b * uint(65538 * 3/13)) / 65536; + blue = (r * uint(65538 * 1/13) + g * uint(65538 * 1/6) + b * uint(65538 * 9/13)) / 65536; + } +}; + +template +static void convertRGBToARGB_helper(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr) +{ + const int offs = bgr ? -1 : 1; + const int w = width * 3; + while (height--) { + uint *dd = dst; + for (int x = 0; x < w; x += 3) { + uchar red = src[x + 1 - offs]; + uchar green = src[x + 1]; + uchar blue = src[x + 1 + offs]; + LcdFilter::filterPixel(red, green, blue); + *dd++ = (0xFF << 24) | (red << 16) | (green << 8) | blue; + } + dst += width; + src += src_pitch; + } +} + +static inline void convertRGBToARGB(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr, bool legacyFilter) +{ + if (!legacyFilter) + convertRGBToARGB_helper(src, dst, width, height, src_pitch, bgr); + else + convertRGBToARGB_helper(src, dst, width, height, src_pitch, bgr); +} + +template +static void convertRGBToARGB_V_helper(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr) +{ + const int offs = bgr ? -src_pitch : src_pitch; + while (height--) { + for (int x = 0; x < width; x++) { + uchar red = src[x + src_pitch - offs]; + uchar green = src[x + src_pitch]; + uchar blue = src[x + src_pitch + offs]; + LcdFilter::filterPixel(red, green, blue); + *dst++ = (0XFF << 24) | (red << 16) | (green << 8) | blue; + } + src += 3*src_pitch; + } +} + +static inline void convertRGBToARGB_V(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr, bool legacyFilter) +{ + if (!legacyFilter) + convertRGBToARGB_V_helper(src, dst, width, height, src_pitch, bgr); + else + convertRGBToARGB_V_helper(src, dst, width, height, src_pitch, bgr); +} + +static inline void convertGRAYToARGB(const uchar *src, uint *dst, int width, int height, int src_pitch) +{ + while (height--) { + const uchar *p = src; + const uchar * const e = p + width; + while (p < e) { + uchar gray = *p++; + *dst++ = (0xFF << 24) | (gray << 16) | (gray << 8) | gray; + } + src += src_pitch; + } +} + +static void convoluteBitmap(const uchar *src, uchar *dst, int width, int height, int pitch) +{ + // convolute the bitmap with a triangle filter to get rid of color fringes + // If we take account for a gamma value of 2, we end up with + // weights of 1, 4, 9, 4, 1. We use an approximation of 1, 3, 8, 3, 1 here, + // as this nicely sums up to 16 :) + int h = height; + while (h--) { + dst[0] = dst[1] = 0; + // + for (int x = 2; x < width - 2; ++x) { + uint sum = src[x-2] + 3*src[x-1] + 8*src[x] + 3*src[x+1] + src[x+2]; + dst[x] = (uchar) (sum >> 4); + } + dst[width - 2] = dst[width - 1] = 0; + src += pitch; + dst += pitch; + } +} + +QFontEngineFT::QFontEngineFT(const QFontDef &fd) + : QFontEngine(Freetype) +{ + fontDef = fd; + matrix.xx = 0x10000; + matrix.yy = 0x10000; + matrix.xy = 0; + matrix.yx = 0; + cache_cost = 100 * 1024; + kerning_pairs_loaded = false; + transform = false; + embolden = false; + obliquen = false; + antialias = true; + freetype = 0; + default_load_flags = FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH; + default_hint_style = ftInitialDefaultHintStyle; + subpixelType = Subpixel_None; + lcdFilterType = 0; +#if defined(FT_LCD_FILTER_H) + lcdFilterType = (int)((quintptr) FT_LCD_FILTER_DEFAULT); +#endif + defaultFormat = Format_None; + embeddedbitmap = false; + const QByteArray env = qgetenv("QT_NO_FT_CACHE"); + cacheEnabled = env.isEmpty() || env.toInt() == 0; + m_subPixelPositionCount = 4; + forceAutoHint = false; +} + +QFontEngineFT::~QFontEngineFT() +{ + if (freetype) + freetype->release(face_id); +} + +bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format, + const QByteArray &fontData) +{ + return init(faceId, antialias, format, QFreetypeFace::getFace(faceId, fontData)); +} + +static void dont_delete(void*) {} + +bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format, + QFreetypeFace *freetypeFace) +{ + freetype = freetypeFace; + if (!freetype) { + xsize = 0; + ysize = 0; + return false; + } + defaultFormat = format; + this->antialias = antialias; + + if (!antialias) + glyphFormat = QFontEngine::Format_Mono; + else + glyphFormat = defaultFormat; + + face_id = faceId; + + symbol = freetype->symbol_map != 0; + PS_FontInfoRec psrec; + // don't assume that type1 fonts are symbol fonts by default + if (FT_Get_PS_Font_Info(freetype->face, &psrec) == FT_Err_Ok) { + symbol = bool(fontDef.family.contains(QLatin1String("symbol"), Qt::CaseInsensitive)); + } + + freetype->computeSize(fontDef, &xsize, &ysize, &defaultGlyphSet.outline_drawing, &scalableBitmapScaleFactor); + + FT_Face face = lockFace(); + + if (FT_IS_SCALABLE(face)) { + bool fake_oblique = (fontDef.style != QFont::StyleNormal) && !(face->style_flags & FT_STYLE_FLAG_ITALIC); + if (fake_oblique) + obliquen = true; + FT_Set_Transform(face, &matrix, 0); + freetype->matrix = matrix; + // fake bold + if ((fontDef.weight >= QFont::Bold) && !(face->style_flags & FT_STYLE_FLAG_BOLD) && !FT_IS_FIXED_WIDTH(face)) { + if (const TT_OS2 *os2 = reinterpret_cast(FT_Get_Sfnt_Table(face, ft_sfnt_os2))) { + if (os2->usWeightClass < 750) + embolden = true; + } + } + // underline metrics + line_thickness = QFixed::fromFixed(FT_MulFix(face->underline_thickness, face->size->metrics.y_scale)); + underline_position = QFixed::fromFixed(-FT_MulFix(face->underline_position, face->size->metrics.y_scale)); + } else { + // ad hoc algorithm + int score = fontDef.weight * fontDef.pixelSize; + line_thickness = score / 700; + // looks better with thicker line for small pointsizes + if (line_thickness < 2 && score >= 1050) + line_thickness = 2; + underline_position = ((line_thickness * 2) + 3) / 6; + + if (isScalableBitmap()) { + glyphFormat = defaultFormat = GlyphFormat::Format_ARGB; + cacheEnabled = false; + } + } + if (line_thickness < 1) + line_thickness = 1; + + metrics = face->size->metrics; + + /* + TrueType fonts with embedded bitmaps may have a bitmap font specific + ascent/descent in the EBLC table. There is no direct public API + to extract those values. The only way we've found is to trick freetype + into thinking that it's not a scalable font in FT_SelectSize so that + the metrics are retrieved from the bitmap strikes. + */ + if (FT_IS_SCALABLE(face)) { + for (int i = 0; i < face->num_fixed_sizes; ++i) { + if (xsize == face->available_sizes[i].x_ppem && ysize == face->available_sizes[i].y_ppem) { + face->face_flags &= ~FT_FACE_FLAG_SCALABLE; + + FT_Select_Size(face, i); + if (face->size->metrics.ascender + face->size->metrics.descender > 0) { + FT_Pos leading = metrics.height - metrics.ascender + metrics.descender; + metrics.ascender = face->size->metrics.ascender; + metrics.descender = face->size->metrics.descender; + if (metrics.descender > 0 + && QString::fromUtf8(face->family_name) == QLatin1String("Courier New")) { + metrics.descender *= -1; + } + metrics.height = metrics.ascender - metrics.descender + leading; + } + FT_Set_Char_Size(face, xsize, ysize, 0, 0); + + face->face_flags |= FT_FACE_FLAG_SCALABLE; + break; + } + } + } + + fontDef.styleName = QString::fromUtf8(face->style_name); + + if (!freetype->hbFace) { + faceData.user_data = face; + faceData.get_font_table = ft_getSfntTable; + (void)harfbuzzFace(); // populates face_ + freetype->hbFace = std::move(face_); + } else { + Q_ASSERT(!face_); + } + // we share the HB face in QFreeTypeFace, so do not let ~QFontEngine() destroy it + face_ = Holder(freetype->hbFace.get(), dont_delete); + + unlockFace(); + + fsType = freetype->fsType(); + return true; +} + +void QFontEngineFT::setQtDefaultHintStyle(QFont::HintingPreference hintingPreference) +{ + switch (hintingPreference) { + case QFont::PreferNoHinting: + setDefaultHintStyle(HintNone); + break; + case QFont::PreferFullHinting: + setDefaultHintStyle(HintFull); + break; + case QFont::PreferVerticalHinting: + setDefaultHintStyle(HintLight); + break; + case QFont::PreferDefaultHinting: + setDefaultHintStyle(ftInitialDefaultHintStyle); + break; + } +} + +void QFontEngineFT::setDefaultHintStyle(HintStyle style) +{ + default_hint_style = style; +} + +int QFontEngineFT::loadFlags(QGlyphSet *set, GlyphFormat format, int flags, + bool &hsubpixel, int &vfactor) const +{ + int load_flags = FT_LOAD_DEFAULT | default_load_flags; + int load_target = default_hint_style == HintLight + ? FT_LOAD_TARGET_LIGHT + : FT_LOAD_TARGET_NORMAL; + + if (format == Format_Mono) { + load_target = FT_LOAD_TARGET_MONO; + } else if (format == Format_A32) { + if (subpixelType == Subpixel_RGB || subpixelType == Subpixel_BGR) { + if (default_hint_style == HintFull) + load_target = FT_LOAD_TARGET_LCD; + hsubpixel = true; + } else if (subpixelType == Subpixel_VRGB || subpixelType == Subpixel_VBGR) { + if (default_hint_style == HintFull) + load_target = FT_LOAD_TARGET_LCD_V; + vfactor = 3; + } + } else if (format == Format_ARGB) { +#ifdef FT_LOAD_COLOR + load_flags |= FT_LOAD_COLOR; +#endif + } + + if (set && set->outline_drawing) + load_flags |= FT_LOAD_NO_BITMAP; + + if (default_hint_style == HintNone || (flags & DesignMetrics) || (set && set->outline_drawing)) + load_flags |= FT_LOAD_NO_HINTING; + else + load_flags |= load_target; + + if (forceAutoHint) + load_flags |= FT_LOAD_FORCE_AUTOHINT; + + return load_flags; +} + +static inline bool areMetricsTooLarge(const QFontEngineFT::GlyphInfo &info) +{ + // false if exceeds QFontEngineFT::Glyph metrics + return (short)(info.linearAdvance) != info.linearAdvance + || (uchar)(info.width) != info.width + || (uchar)(info.height) != info.height; +} + +static inline void transformBoundingBox(int *left, int *top, int *right, int *bottom, FT_Matrix *matrix) +{ + int l, r, t, b; + FT_Vector vector; + vector.x = *left; + vector.y = *top; + FT_Vector_Transform(&vector, matrix); + l = r = vector.x; + t = b = vector.y; + vector.x = *right; + vector.y = *top; + FT_Vector_Transform(&vector, matrix); + if (l > vector.x) l = vector.x; + if (r < vector.x) r = vector.x; + if (t < vector.y) t = vector.y; + if (b > vector.y) b = vector.y; + vector.x = *right; + vector.y = *bottom; + FT_Vector_Transform(&vector, matrix); + if (l > vector.x) l = vector.x; + if (r < vector.x) r = vector.x; + if (t < vector.y) t = vector.y; + if (b > vector.y) b = vector.y; + vector.x = *left; + vector.y = *bottom; + FT_Vector_Transform(&vector, matrix); + if (l > vector.x) l = vector.x; + if (r < vector.x) r = vector.x; + if (t < vector.y) t = vector.y; + if (b > vector.y) b = vector.y; + *left = l; + *right = r; + *top = t; + *bottom = b; +} + +QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, + QFixed subPixelPosition, + GlyphFormat format, + bool fetchMetricsOnly, + bool disableOutlineDrawing) const +{ +// Q_ASSERT(freetype->lock == 1); + + if (format == Format_None) + format = defaultFormat != Format_None ? defaultFormat : Format_Mono; + Q_ASSERT(format != Format_None); + + Glyph *g = set ? set->getGlyph(glyph, subPixelPosition) : 0; + if (g && g->format == format && (fetchMetricsOnly || g->data)) + return g; + + if (!g && set && set->isGlyphMissing(glyph)) + return &emptyGlyph; + + + FT_Face face = freetype->face; + + FT_Matrix matrix = freetype->matrix; + + FT_Vector v; + v.x = format == Format_Mono ? 0 : FT_Pos(subPixelPosition.value()); + v.y = 0; + FT_Set_Transform(face, &matrix, &v); + + bool hsubpixel = false; + int vfactor = 1; + int load_flags = loadFlags(set, format, 0, hsubpixel, vfactor); + + bool transform = matrix.xx != 0x10000 + || matrix.yy != 0x10000 + || matrix.xy != 0 + || matrix.yx != 0; + + if (transform || (format != Format_Mono && !isScalableBitmap())) + load_flags |= FT_LOAD_NO_BITMAP; + + FT_Error err = FT_Load_Glyph(face, glyph, load_flags); + if (err && (load_flags & FT_LOAD_NO_BITMAP)) { + load_flags &= ~FT_LOAD_NO_BITMAP; + err = FT_Load_Glyph(face, glyph, load_flags); + } + if (err == FT_Err_Too_Few_Arguments) { + // this is an error in the bytecode interpreter, just try to run without it + load_flags |= FT_LOAD_FORCE_AUTOHINT; + err = FT_Load_Glyph(face, glyph, load_flags); + } else if (err == FT_Err_Execution_Too_Long) { + // This is an error in the bytecode, probably a web font made by someone who + // didn't test bytecode hinting at all so disable for it for all glyphs. + qWarning("load glyph failed due to broken hinting bytecode in font, switching to auto hinting"); + default_load_flags |= FT_LOAD_FORCE_AUTOHINT; + load_flags |= FT_LOAD_FORCE_AUTOHINT; + err = FT_Load_Glyph(face, glyph, load_flags); + } + if (err != FT_Err_Ok) { + qWarning("load glyph failed err=%x face=%p, glyph=%d", err, face, glyph); + if (set) + set->setGlyphMissing(glyph); + return &emptyGlyph; + } + + FT_GlyphSlot slot = face->glyph; + + if (embolden) + FT_GlyphSlot_Embolden(slot); + if (obliquen) { + FT_GlyphSlot_Oblique(slot); + + // While Embolden alters the metrics of the slot, oblique does not, so we need + // to fix this ourselves. + transform = true; + FT_Matrix m; + m.xx = 0x10000; + m.yx = 0x0; + m.xy = 0x6000; + m.yy = 0x10000; + + FT_Matrix_Multiply(&m, &matrix); + } + + GlyphInfo info; + info.linearAdvance = slot->linearHoriAdvance >> 10; + info.xOff = TRUNC(ROUND(slot->advance.x)); + info.yOff = 0; + + if ((set && set->outline_drawing && !disableOutlineDrawing) || fetchMetricsOnly) { + int left = slot->metrics.horiBearingX; + int right = slot->metrics.horiBearingX + slot->metrics.width; + int top = slot->metrics.horiBearingY; + int bottom = slot->metrics.horiBearingY - slot->metrics.height; + + if (transform && slot->format != FT_GLYPH_FORMAT_BITMAP) + transformBoundingBox(&left, &top, &right, &bottom, &matrix); + + left = FLOOR(left); + right = CEIL(right); + bottom = FLOOR(bottom); + top = CEIL(top); + + info.x = TRUNC(left); + info.y = TRUNC(top); + info.width = TRUNC(right - left); + info.height = TRUNC(top - bottom); + + // If any of the metrics are too large to fit, don't cache them + if (areMetricsTooLarge(info)) + return 0; + + g = new Glyph; + g->data = 0; + g->linearAdvance = info.linearAdvance; + g->width = info.width; + g->height = info.height; + g->x = info.x; + g->y = info.y; + g->advance = info.xOff; + g->format = format; + + if (set) + set->setGlyph(glyph, subPixelPosition, g); + + return g; + } + + int glyph_buffer_size = 0; + QScopedArrayPointer glyph_buffer; +#if defined(QT_USE_FREETYPE_LCDFILTER) + bool useFreetypeRenderGlyph = false; + if (slot->format == FT_GLYPH_FORMAT_OUTLINE && (hsubpixel || vfactor != 1)) { + err = FT_Library_SetLcdFilter(slot->library, (FT_LcdFilter)lcdFilterType); + if (err == FT_Err_Ok) + useFreetypeRenderGlyph = true; + } + + if (useFreetypeRenderGlyph) { + err = FT_Render_Glyph(slot, hsubpixel ? FT_RENDER_MODE_LCD : FT_RENDER_MODE_LCD_V); + + if (err != FT_Err_Ok) + qWarning("render glyph failed err=%x face=%p, glyph=%d", err, face, glyph); + + FT_Library_SetLcdFilter(slot->library, FT_LCD_FILTER_NONE); + + info.height = slot->bitmap.rows / vfactor; + info.width = hsubpixel ? slot->bitmap.width / 3 : slot->bitmap.width; + info.x = slot->bitmap_left; + info.y = slot->bitmap_top; + + glyph_buffer_size = info.width * info.height * 4; + glyph_buffer.reset(new uchar[glyph_buffer_size]); + + if (hsubpixel) + convertRGBToARGB(slot->bitmap.buffer, (uint *)glyph_buffer.data(), info.width, info.height, slot->bitmap.pitch, subpixelType != Subpixel_RGB, false); + else if (vfactor != 1) + convertRGBToARGB_V(slot->bitmap.buffer, (uint *)glyph_buffer.data(), info.width, info.height, slot->bitmap.pitch, subpixelType != Subpixel_VRGB, false); + } else +#endif + { + int left = slot->metrics.horiBearingX; + int right = slot->metrics.horiBearingX + slot->metrics.width; + int top = slot->metrics.horiBearingY; + int bottom = slot->metrics.horiBearingY - slot->metrics.height; + if (transform && slot->format != FT_GLYPH_FORMAT_BITMAP) + transformBoundingBox(&left, &top, &right, &bottom, &matrix); + left = FLOOR(left); + right = CEIL(right); + bottom = FLOOR(bottom); + top = CEIL(top); + + int hpixels = TRUNC(right - left); + // subpixel position requires one more pixel + if (subPixelPosition > 0 && format != Format_Mono) + hpixels++; + + if (hsubpixel) + hpixels = hpixels*3 + 8; + info.width = hpixels; + info.height = TRUNC(top - bottom); + info.x = TRUNC(left); + info.y = TRUNC(top); + if (hsubpixel) { + info.width /= 3; + info.x -= 1; + } + + // If any of the metrics are too large to fit, don't cache them + if (areMetricsTooLarge(info)) + return 0; + + int pitch = (format == Format_Mono ? ((info.width + 31) & ~31) >> 3 : + (format == Format_A8 ? (info.width + 3) & ~3 : info.width * 4)); + if (glyph_buffer_size < pitch * info.height) { + glyph_buffer_size = pitch * info.height; + glyph_buffer.reset(new uchar[glyph_buffer_size]); + memset(glyph_buffer.data(), 0, glyph_buffer_size); + } + + if (slot->format == FT_GLYPH_FORMAT_OUTLINE) { + FT_Bitmap bitmap; + bitmap.rows = info.height*vfactor; + bitmap.width = hpixels; + bitmap.pitch = format == Format_Mono ? (((info.width + 31) & ~31) >> 3) : ((bitmap.width + 3) & ~3); + int bitmap_buffer_size = bitmap.rows * bitmap.pitch; + if (!hsubpixel && vfactor == 1 && format != Format_A32) { + Q_ASSERT(glyph_buffer_size <= bitmap_buffer_size); + bitmap.buffer = glyph_buffer.data(); + } else { + bitmap.buffer = new uchar[bitmap_buffer_size]; + memset(bitmap.buffer, 0, bitmap_buffer_size); + } + bitmap.pixel_mode = format == Format_Mono ? FT_PIXEL_MODE_MONO : FT_PIXEL_MODE_GRAY; + FT_Matrix matrix; + matrix.xx = (hsubpixel ? 3 : 1) << 16; + matrix.yy = vfactor << 16; + matrix.yx = matrix.xy = 0; + + FT_Outline_Transform(&slot->outline, &matrix); + FT_Outline_Translate (&slot->outline, (hsubpixel ? -3*left +(4<<6) : -left), -bottom*vfactor); + FT_Outline_Get_Bitmap(slot->library, &slot->outline, &bitmap); + if (hsubpixel) { + Q_ASSERT (bitmap.pixel_mode == FT_PIXEL_MODE_GRAY); + Q_ASSERT(antialias); + uchar *convoluted = new uchar[bitmap_buffer_size]; + bool useLegacyLcdFilter = false; +#if defined(FC_LCD_FILTER) && defined(FT_LCD_FILTER_H) + useLegacyLcdFilter = (lcdFilterType == FT_LCD_FILTER_LEGACY); +#endif + uchar *buffer = bitmap.buffer; + if (!useLegacyLcdFilter) { + convoluteBitmap(bitmap.buffer, convoluted, bitmap.width, info.height, bitmap.pitch); + buffer = convoluted; + } + convertRGBToARGB(buffer + 1, (uint *)glyph_buffer.data(), info.width, info.height, bitmap.pitch, subpixelType != Subpixel_RGB, useLegacyLcdFilter); + delete [] convoluted; + } else if (vfactor != 1) { + convertRGBToARGB_V(bitmap.buffer, (uint *)glyph_buffer.data(), info.width, info.height, bitmap.pitch, subpixelType != Subpixel_VRGB, true); + } else if (format == Format_A32 && bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) { + convertGRAYToARGB(bitmap.buffer, (uint *)glyph_buffer.data(), info.width, info.height, bitmap.pitch); + } + + if (bitmap.buffer != glyph_buffer.data()) + delete [] bitmap.buffer; + } else if (slot->format == FT_GLYPH_FORMAT_BITMAP) { +#if ((FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100) >= 20500) + Q_ASSERT(slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO || slot->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA); +#else + Q_ASSERT(slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO); +#endif + uchar *src = slot->bitmap.buffer; + uchar *dst = glyph_buffer.data(); + int h = slot->bitmap.rows; + if (format == Format_Mono) { + int bytes = ((info.width + 7) & ~7) >> 3; + while (h--) { + memcpy (dst, src, bytes); + dst += pitch; + src += slot->bitmap.pitch; + } + } else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) { + if (hsubpixel) { + while (h--) { + uint *dd = (uint *)dst; + *dd++ = 0; + for (int x = 0; x < static_cast(slot->bitmap.width); x++) { + uint a = ((src[x >> 3] & (0x80 >> (x & 7))) ? 0xffffff : 0x000000); + *dd++ = a; + } + *dd++ = 0; + dst += pitch; + src += slot->bitmap.pitch; + } + } else if (vfactor != 1) { + while (h--) { + uint *dd = (uint *)dst; + for (int x = 0; x < static_cast(slot->bitmap.width); x++) { + uint a = ((src[x >> 3] & (0x80 >> (x & 7))) ? 0xffffff : 0x000000); + *dd++ = a; + } + dst += pitch; + src += slot->bitmap.pitch; + } + } else { + while (h--) { + for (int x = 0; x < static_cast(slot->bitmap.width); x++) { + unsigned char a = ((src[x >> 3] & (0x80 >> (x & 7))) ? 0xff : 0x00); + dst[x] = a; + } + dst += pitch; + src += slot->bitmap.pitch; + } + } + } +#if ((FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100) >= 20500) + else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) + { + while (h--) { +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + const quint32 *srcPixel = (const quint32 *)src; + quint32 *dstPixel = (quint32 *)dst; + for (int x = 0; x < static_cast(slot->bitmap.width); x++, srcPixel++, dstPixel++) { + const quint32 pixel = *srcPixel; + *dstPixel = qbswap(pixel); + } +#else + memcpy(dst, src, slot->bitmap.width * 4); +#endif + dst += slot->bitmap.pitch; + src += slot->bitmap.pitch; + } + info.width = info.linearAdvance = info.xOff = slot->bitmap.width; + info.height = slot->bitmap.rows; + info.x = slot->bitmap_left; + info.y = slot->bitmap_top; + } +#endif + } else { + qWarning("QFontEngine: Glyph neither outline nor bitmap format=%d", slot->format); + return 0; + } + } + + + if (!g) { + g = new Glyph; + g->data = 0; + } + + g->linearAdvance = info.linearAdvance; + g->width = info.width; + g->height = info.height; + g->x = info.x; + g->y = info.y; + g->advance = info.xOff; + g->format = format; + delete [] g->data; + g->data = glyph_buffer.take(); + + if (set) + set->setGlyph(glyph, subPixelPosition, g); + + return g; +} + +QFontEngine::FaceId QFontEngineFT::faceId() const +{ + return face_id; +} + +QFontEngine::Properties QFontEngineFT::properties() const +{ + Properties p = freetype->properties(); + if (p.postscriptName.isEmpty()) { + p.postscriptName = QFontEngine::convertToPostscriptFontFamilyName(fontDef.family.toUtf8()); + } + + return freetype->properties(); +} + +QFixed QFontEngineFT::emSquareSize() const +{ + if (FT_IS_SCALABLE(freetype->face)) + return freetype->face->units_per_EM; + else + return freetype->face->size->metrics.y_ppem; +} + +bool QFontEngineFT::getSfntTableData(uint tag, uchar *buffer, uint *length) const +{ + return ft_getSfntTable(freetype->face, tag, buffer, length); +} + +int QFontEngineFT::synthesized() const +{ + int s = 0; + if ((fontDef.style != QFont::StyleNormal) && !(freetype->face->style_flags & FT_STYLE_FLAG_ITALIC)) + s = SynthesizedItalic; + if ((fontDef.weight >= QFont::Bold) && !(freetype->face->style_flags & FT_STYLE_FLAG_BOLD)) + s |= SynthesizedBold; + if (fontDef.stretch != 100 && FT_IS_SCALABLE(freetype->face)) + s |= SynthesizedStretch; + return s; +} + +QFixed QFontEngineFT::ascent() const +{ + QFixed v = QFixed::fromFixed(metrics.ascender); + if (scalableBitmapScaleFactor != 1) + v *= scalableBitmapScaleFactor; + return v; +} + +QFixed QFontEngineFT::capHeight() const +{ + TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2); + if (os2 && os2->version >= 2) { + lockFace(); + QFixed answer = QFixed::fromFixed(FT_MulFix(os2->sCapHeight, freetype->face->size->metrics.y_scale)); + unlockFace(); + return answer; + } + return calculatedCapHeight(); +} + +QFixed QFontEngineFT::descent() const +{ + QFixed v = QFixed::fromFixed(-metrics.descender); + if (scalableBitmapScaleFactor != 1) + v *= scalableBitmapScaleFactor; + return v; +} + +QFixed QFontEngineFT::leading() const +{ + QFixed v = QFixed::fromFixed(metrics.height - metrics.ascender + metrics.descender); + if (scalableBitmapScaleFactor != 1) + v *= scalableBitmapScaleFactor; + return v; +} + +QFixed QFontEngineFT::xHeight() const +{ + TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2); + if (os2 && os2->sxHeight) { + lockFace(); + QFixed answer = QFixed(os2->sxHeight * freetype->face->size->metrics.y_ppem) / emSquareSize(); + unlockFace(); + return answer; + } + + return QFontEngine::xHeight(); +} + +QFixed QFontEngineFT::averageCharWidth() const +{ + TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2); + if (os2 && os2->xAvgCharWidth) { + lockFace(); + QFixed answer = QFixed(os2->xAvgCharWidth * freetype->face->size->metrics.x_ppem) / emSquareSize(); + unlockFace(); + return answer; + } + + return QFontEngine::averageCharWidth(); +} + +qreal QFontEngineFT::maxCharWidth() const +{ + QFixed max_advance = QFixed::fromFixed(metrics.max_advance); + if (scalableBitmapScaleFactor != 1) + max_advance *= scalableBitmapScaleFactor; + return max_advance.toReal(); +} + +QFixed QFontEngineFT::lineThickness() const +{ + return line_thickness; +} + +QFixed QFontEngineFT::underlinePosition() const +{ + return underline_position; +} + +void QFontEngineFT::doKerning(QGlyphLayout *g, QFontEngine::ShaperFlags flags) const +{ + if (!kerning_pairs_loaded) { + kerning_pairs_loaded = true; + lockFace(); + if (freetype->face->size->metrics.x_ppem != 0) { + QFixed scalingFactor = emSquareSize() / QFixed(freetype->face->size->metrics.x_ppem); + unlockFace(); + const_cast(this)->loadKerningPairs(scalingFactor); + } else { + unlockFace(); + } + } + + if (shouldUseDesignMetrics(flags) && !(fontDef.styleStrategy & QFont::ForceIntegerMetrics)) + flags |= DesignMetrics; + else + flags &= ~DesignMetrics; + + QFontEngine::doKerning(g, flags); +} + +static inline FT_Matrix QTransformToFTMatrix(const QTransform &matrix) +{ + FT_Matrix m; + + m.xx = FT_Fixed(matrix.m11() * 65536); + m.xy = FT_Fixed(-matrix.m21() * 65536); + m.yx = FT_Fixed(-matrix.m12() * 65536); + m.yy = FT_Fixed(matrix.m22() * 65536); + + return m; +} + +QFontEngineFT::QGlyphSet *QFontEngineFT::loadGlyphSet(const QTransform &matrix) +{ + if (matrix.type() > QTransform::TxShear || !cacheEnabled) + return 0; + + // FT_Set_Transform only supports scalable fonts + if (!FT_IS_SCALABLE(freetype->face)) + return matrix.type() <= QTransform::TxTranslate ? &defaultGlyphSet : Q_NULLPTR; + + FT_Matrix m = QTransformToFTMatrix(matrix); + + QGlyphSet *gs = 0; + + for (int i = 0; i < transformedGlyphSets.count(); ++i) { + const QGlyphSet &g = transformedGlyphSets.at(i); + if (g.transformationMatrix.xx == m.xx + && g.transformationMatrix.xy == m.xy + && g.transformationMatrix.yx == m.yx + && g.transformationMatrix.yy == m.yy) { + + // found a match, move it to the front + transformedGlyphSets.move(i, 0); + gs = &transformedGlyphSets[0]; + break; + } + } + + if (!gs) { + // don't cache more than 10 transformations + if (transformedGlyphSets.count() >= 10) { + transformedGlyphSets.move(transformedGlyphSets.size() - 1, 0); + } else { + transformedGlyphSets.prepend(QGlyphSet()); + } + gs = &transformedGlyphSets[0]; + gs->clear(); + gs->transformationMatrix = m; + gs->outline_drawing = fontDef.pixelSize * fontDef.pixelSize * qAbs(matrix.det()) >= QT_MAX_CACHED_GLYPH_SIZE * QT_MAX_CACHED_GLYPH_SIZE; + } + Q_ASSERT(gs != 0); + + return gs; +} + +void QFontEngineFT::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics) +{ + FT_Face face = lockFace(Unscaled); + FT_Set_Transform(face, 0, 0); + FT_Load_Glyph(face, glyph, FT_LOAD_NO_BITMAP); + + int left = face->glyph->metrics.horiBearingX; + int right = face->glyph->metrics.horiBearingX + face->glyph->metrics.width; + int top = face->glyph->metrics.horiBearingY; + int bottom = face->glyph->metrics.horiBearingY - face->glyph->metrics.height; + + QFixedPoint p; + p.x = 0; + p.y = 0; + + metrics->width = QFixed::fromFixed(right-left); + metrics->height = QFixed::fromFixed(top-bottom); + metrics->x = QFixed::fromFixed(left); + metrics->y = QFixed::fromFixed(-top); + metrics->xoff = QFixed::fromFixed(face->glyph->advance.x); + + if (!FT_IS_SCALABLE(freetype->face)) + QFreetypeFace::addBitmapToPath(face->glyph, p, path); + else + QFreetypeFace::addGlyphToPath(face, face->glyph, p, path, face->units_per_EM << 6, face->units_per_EM << 6); + + FT_Set_Transform(face, &freetype->matrix, 0); + unlockFace(); +} + +bool QFontEngineFT::supportsTransformation(const QTransform &transform) const +{ + return transform.type() <= QTransform::TxRotate; +} + +void QFontEngineFT::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags) +{ + if (!glyphs.numGlyphs) + return; + + if (FT_IS_SCALABLE(freetype->face)) { + QFontEngine::addOutlineToPath(x, y, glyphs, path, flags); + } else { + QVarLengthArray positions; + QVarLengthArray positioned_glyphs; + QTransform matrix; + matrix.translate(x, y); + getGlyphPositions(glyphs, matrix, flags, positioned_glyphs, positions); + + FT_Face face = lockFace(Unscaled); + for (int gl = 0; gl < glyphs.numGlyphs; gl++) { + FT_UInt glyph = positioned_glyphs[gl]; + FT_Load_Glyph(face, glyph, FT_LOAD_TARGET_MONO); + QFreetypeFace::addBitmapToPath(face->glyph, positions[gl], path); + } + unlockFace(); + } +} + +void QFontEngineFT::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int numGlyphs, + QPainterPath *path, QTextItem::RenderFlags) +{ + FT_Face face = lockFace(Unscaled); + + for (int gl = 0; gl < numGlyphs; gl++) { + FT_UInt glyph = glyphs[gl]; + + FT_Load_Glyph(face, glyph, FT_LOAD_NO_BITMAP); + + FT_GlyphSlot g = face->glyph; + if (g->format != FT_GLYPH_FORMAT_OUTLINE) + continue; + if (embolden) + FT_GlyphSlot_Embolden(g); + if (obliquen) + FT_GlyphSlot_Oblique(g); + QFreetypeFace::addGlyphToPath(face, g, positions[gl], path, xsize, ysize); + } + unlockFace(); +} + +glyph_t QFontEngineFT::glyphIndex(uint ucs4) const +{ + glyph_t glyph = ucs4 < QFreetypeFace::cmapCacheSize ? freetype->cmapCache[ucs4] : 0; + if (glyph == 0) { + FT_Face face = freetype->face; + glyph = FT_Get_Char_Index(face, ucs4); + if (glyph == 0) { + // Certain fonts don't have no-break space and tab, + // while we usually want to render them as space + if (ucs4 == QChar::Nbsp || ucs4 == QChar::Tabulation) { + glyph = FT_Get_Char_Index(face, QChar::Space); + } else if (freetype->symbol_map) { + // Symbol fonts can have more than one CMAPs, FreeType should take the + // correct one for us by default, so we always try FT_Get_Char_Index + // first. If it didn't work (returns 0), we will explicitly set the + // CMAP to symbol font one and try again. symbol_map is not always the + // correct one because in certain fonts like Wingdings symbol_map only + // contains PUA codepoints instead of the common ones. + FT_Set_Charmap(face, freetype->symbol_map); + glyph = FT_Get_Char_Index(face, ucs4); + FT_Set_Charmap(face, freetype->unicode_map); + } + } + if (ucs4 < QFreetypeFace::cmapCacheSize) + freetype->cmapCache[ucs4] = glyph; + } + + return glyph; +} + +bool QFontEngineFT::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, + QFontEngine::ShaperFlags flags) const +{ + Q_ASSERT(glyphs->numGlyphs >= *nglyphs); + if (*nglyphs < len) { + *nglyphs = len; + return false; + } + + int glyph_pos = 0; + if (freetype->symbol_map) { + FT_Face face = freetype->face; + QStringIterator it(str, str + len); + while (it.hasNext()) { + uint uc = it.next(); + glyphs->glyphs[glyph_pos] = uc < QFreetypeFace::cmapCacheSize ? freetype->cmapCache[uc] : 0; + if ( !glyphs->glyphs[glyph_pos] ) { + // Symbol fonts can have more than one CMAPs, FreeType should take the + // correct one for us by default, so we always try FT_Get_Char_Index + // first. If it didn't work (returns 0), we will explicitly set the + // CMAP to symbol font one and try again. symbol_map is not always the + // correct one because in certain fonts like Wingdings symbol_map only + // contains PUA codepoints instead of the common ones. + glyph_t glyph = FT_Get_Char_Index(face, uc); + // Certain symbol fonts don't have no-break space (0xa0) and tab (0x9), + // while we usually want to render them as space + if (!glyph && (uc == 0xa0 || uc == 0x9)) { + uc = 0x20; + glyph = FT_Get_Char_Index(face, uc); + } + if (!glyph) { + FT_Set_Charmap(face, freetype->symbol_map); + glyph = FT_Get_Char_Index(face, uc); + FT_Set_Charmap(face, freetype->unicode_map); + } + glyphs->glyphs[glyph_pos] = glyph; + if (uc < QFreetypeFace::cmapCacheSize) + freetype->cmapCache[uc] = glyph; + } + ++glyph_pos; + } + } else { + FT_Face face = freetype->face; + QStringIterator it(str, str + len); + while (it.hasNext()) { + uint uc = it.next(); + glyphs->glyphs[glyph_pos] = uc < QFreetypeFace::cmapCacheSize ? freetype->cmapCache[uc] : 0; + if (!glyphs->glyphs[glyph_pos]) { + { + redo: + glyph_t glyph = FT_Get_Char_Index(face, uc); + if (!glyph && (uc == 0xa0 || uc == 0x9)) { + uc = 0x20; + goto redo; + } + glyphs->glyphs[glyph_pos] = glyph; + if (uc < QFreetypeFace::cmapCacheSize) + freetype->cmapCache[uc] = glyph; + } + } + ++glyph_pos; + } + } + + *nglyphs = glyph_pos; + glyphs->numGlyphs = glyph_pos; + + if (!(flags & GlyphIndicesOnly)) + recalcAdvances(glyphs, flags); + + return true; +} + +bool QFontEngineFT::shouldUseDesignMetrics(QFontEngine::ShaperFlags flags) const +{ + if (!FT_IS_SCALABLE(freetype->face)) + return false; + + return default_hint_style == HintNone || default_hint_style == HintLight || (flags & DesignMetrics); +} + +QFixed QFontEngineFT::scaledBitmapMetrics(QFixed m) const +{ + return m * scalableBitmapScaleFactor; +} + +glyph_metrics_t QFontEngineFT::scaledBitmapMetrics(const glyph_metrics_t &m) const +{ + glyph_metrics_t metrics; + metrics.x = scaledBitmapMetrics(m.x); + metrics.y = scaledBitmapMetrics(m.y); + metrics.width = scaledBitmapMetrics(m.width); + metrics.height = scaledBitmapMetrics(m.height); + metrics.xoff = scaledBitmapMetrics(m.xoff); + metrics.yoff = scaledBitmapMetrics(m.yoff); + return metrics; +} + +void QFontEngineFT::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const +{ + FT_Face face = 0; + bool design = shouldUseDesignMetrics(flags); + for (int i = 0; i < glyphs->numGlyphs; i++) { + Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(glyphs->glyphs[i]) : 0; + // Since we are passing Format_None to loadGlyph, use same default format logic as loadGlyph + GlyphFormat acceptableFormat = (defaultFormat != Format_None) ? defaultFormat : Format_Mono; + if (g && g->format == acceptableFormat) { + glyphs->advances[i] = design ? QFixed::fromFixed(g->linearAdvance) : QFixed(g->advance); + } else { + if (!face) + face = lockFace(); + g = loadGlyph(cacheEnabled ? &defaultGlyphSet : 0, glyphs->glyphs[i], 0, Format_None, true); + if (g) + glyphs->advances[i] = design ? QFixed::fromFixed(g->linearAdvance) : QFixed(g->advance); + else + glyphs->advances[i] = design ? QFixed::fromFixed(face->glyph->linearHoriAdvance >> 10) + : QFixed::fromFixed(face->glyph->metrics.horiAdvance).round(); + if (!cacheEnabled && g != &emptyGlyph) + delete g; + } + + if (scalableBitmapScaleFactor != 1) + glyphs->advances[i] *= scalableBitmapScaleFactor; + } + if (face) + unlockFace(); + + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { + for (int i = 0; i < glyphs->numGlyphs; ++i) + glyphs->advances[i] = glyphs->advances[i].round(); + } +} + +glyph_metrics_t QFontEngineFT::boundingBox(const QGlyphLayout &glyphs) +{ + FT_Face face = 0; + + glyph_metrics_t overall; + // initialize with line height, we get the same behaviour on all platforms + if (!isScalableBitmap()) { + overall.y = -ascent(); + overall.height = ascent() + descent(); + } else { + overall.y = QFixed::fromFixed(-metrics.ascender); + overall.height = QFixed::fromFixed(metrics.ascender - metrics.descender); + } + + QFixed ymax = 0; + QFixed xmax = 0; + for (int i = 0; i < glyphs.numGlyphs; i++) { + Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(glyphs.glyphs[i]) : 0; + if (!g) { + if (!face) + face = lockFace(); + g = loadGlyph(cacheEnabled ? &defaultGlyphSet : 0, glyphs.glyphs[i], 0, Format_None, true); + } + if (g) { + QFixed x = overall.xoff + glyphs.offsets[i].x + g->x; + QFixed y = overall.yoff + glyphs.offsets[i].y - g->y; + overall.x = qMin(overall.x, x); + overall.y = qMin(overall.y, y); + xmax = qMax(xmax, x + g->width); + ymax = qMax(ymax, y + g->height); + overall.xoff += g->advance; + if (!cacheEnabled && g != &emptyGlyph) + delete g; + } else { + int left = FLOOR(face->glyph->metrics.horiBearingX); + int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width); + int top = CEIL(face->glyph->metrics.horiBearingY); + int bottom = FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height); + + QFixed x = overall.xoff + glyphs.offsets[i].x - (-TRUNC(left)); + QFixed y = overall.yoff + glyphs.offsets[i].y - TRUNC(top); + overall.x = qMin(overall.x, x); + overall.y = qMin(overall.y, y); + xmax = qMax(xmax, x + TRUNC(right - left)); + ymax = qMax(ymax, y + TRUNC(top - bottom)); + overall.xoff += int(TRUNC(ROUND(face->glyph->advance.x))); + } + } + overall.height = qMax(overall.height, ymax - overall.y); + overall.width = xmax - overall.x; + + if (face) + unlockFace(); + + if (isScalableBitmap()) + overall = scaledBitmapMetrics(overall); + return overall; +} + +glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph) +{ + FT_Face face = 0; + glyph_metrics_t overall; + Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(glyph) : 0; + if (!g) { + face = lockFace(); + g = loadGlyph(cacheEnabled ? &defaultGlyphSet : 0, glyph, 0, Format_None, true); + } + if (g) { + overall.x = g->x; + overall.y = -g->y; + overall.width = g->width; + overall.height = g->height; + overall.xoff = g->advance; + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + overall.xoff = overall.xoff.round(); + if (!cacheEnabled && g != &emptyGlyph) + delete g; + } else { + int left = FLOOR(face->glyph->metrics.horiBearingX); + int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width); + int top = CEIL(face->glyph->metrics.horiBearingY); + int bottom = FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height); + + overall.width = TRUNC(right-left); + overall.height = TRUNC(top-bottom); + overall.x = TRUNC(left); + overall.y = -TRUNC(top); + overall.xoff = TRUNC(ROUND(face->glyph->advance.x)); + } + if (face) + unlockFace(); + + if (isScalableBitmap()) + overall = scaledBitmapMetrics(overall); + return overall; +} + +glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph, const QTransform &matrix) +{ + return alphaMapBoundingBox(glyph, 0, matrix, QFontEngine::Format_None); +} + +glyph_metrics_t QFontEngineFT::alphaMapBoundingBox(glyph_t glyph, QFixed subPixelPosition, const QTransform &matrix, QFontEngine::GlyphFormat format) +{ + Glyph *g = loadGlyphFor(glyph, subPixelPosition, format, matrix, true); + + glyph_metrics_t overall; + if (g) { + overall.x = g->x; + overall.y = -g->y; + overall.width = g->width; + overall.height = g->height; + overall.xoff = g->advance; + if (!cacheEnabled && g != &emptyGlyph) + delete g; + } else { + FT_Face face = lockFace(); + int left = FLOOR(face->glyph->metrics.horiBearingX); + int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width); + int top = CEIL(face->glyph->metrics.horiBearingY); + int bottom = FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height); + + overall.width = TRUNC(right-left); + overall.height = TRUNC(top-bottom); + overall.x = TRUNC(left); + overall.y = -TRUNC(top); + overall.xoff = TRUNC(ROUND(face->glyph->advance.x)); + unlockFace(); + } + + if (isScalableBitmap()) + overall = scaledBitmapMetrics(overall); + return overall; +} + +static inline QImage alphaMapFromGlyphData(QFontEngineFT::Glyph *glyph, QFontEngine::GlyphFormat glyphFormat) +{ + if (glyph == Q_NULLPTR || glyph->height == 0 || glyph->width == 0) + return QImage(); + + QImage::Format format = QImage::Format_Invalid; + int bytesPerLine = -1; + switch (glyphFormat) { + case QFontEngine::Format_Mono: + format = QImage::Format_Mono; + bytesPerLine = ((glyph->width + 31) & ~31) >> 3; + break; + case QFontEngine::Format_A8: + format = QImage::Format_Alpha8; + bytesPerLine = (glyph->width + 3) & ~3; + break; + case QFontEngine::Format_A32: + format = QImage::Format_RGB32; + bytesPerLine = glyph->width * 4; + break; + default: + Q_UNREACHABLE(); + }; + + QImage img(static_cast(glyph->data), glyph->width, glyph->height, bytesPerLine, format); + if (format == QImage::Format_Mono) + img.setColor(1, QColor(Qt::white).rgba()); // Expands color table to 2 items; item 0 set to transparent. + return img; +} + +QImage *QFontEngineFT::lockedAlphaMapForGlyph(glyph_t glyphIndex, QFixed subPixelPosition, + QFontEngine::GlyphFormat neededFormat, + const QTransform &t, QPoint *offset) +{ + Q_ASSERT(currentlyLockedAlphaMap.isNull()); + + if (isBitmapFont()) + neededFormat = Format_Mono; + else if (neededFormat == Format_None && defaultFormat != Format_None) + neededFormat = defaultFormat; + else if (neededFormat == Format_None) + neededFormat = Format_A8; + + Glyph *glyph = loadGlyphFor(glyphIndex, subPixelPosition, neededFormat, t); + + if (offset != 0 && glyph != 0) + *offset = QPoint(glyph->x, -glyph->y); + + currentlyLockedAlphaMap = alphaMapFromGlyphData(glyph, neededFormat); + + const bool glyphHasGeometry = glyph != Q_NULLPTR && glyph->height != 0 && glyph->width != 0; + if (!cacheEnabled && glyph != &emptyGlyph) { + currentlyLockedAlphaMap = currentlyLockedAlphaMap.copy(); + delete glyph; + } + + if (!glyphHasGeometry) + return Q_NULLPTR; + + if (currentlyLockedAlphaMap.isNull()) + return QFontEngine::lockedAlphaMapForGlyph(glyphIndex, subPixelPosition, neededFormat, t, offset); + + QImageData *data = currentlyLockedAlphaMap.data_ptr(); + data->is_locked = true; + + return ¤tlyLockedAlphaMap; +} + +void QFontEngineFT::unlockAlphaMapForGlyph() +{ + QFontEngine::unlockAlphaMapForGlyph(); +} + +static inline bool is2dRotation(const QTransform &t) +{ + return qFuzzyCompare(t.m11(), t.m22()) && qFuzzyCompare(t.m12(), -t.m21()) + && qFuzzyCompare(t.m11()*t.m22() - t.m12()*t.m21(), qreal(1.0)); +} + +QFontEngineFT::Glyph *QFontEngineFT::loadGlyphFor(glyph_t g, + QFixed subPixelPosition, + GlyphFormat format, + const QTransform &t, + bool fetchBoundingBox, + bool disableOutlineDrawing) +{ + QGlyphSet *glyphSet = loadGlyphSet(t); + if (glyphSet != 0 && glyphSet->outline_drawing && !disableOutlineDrawing && !fetchBoundingBox) + return 0; + + Glyph *glyph = glyphSet != 0 ? glyphSet->getGlyph(g, subPixelPosition) : 0; + if (!glyph || glyph->format != format || (!fetchBoundingBox && !glyph->data)) { + QScopedValueRollback saved_default_hint_style(default_hint_style); + if (t.type() >= QTransform::TxScale && !is2dRotation(t)) + default_hint_style = HintNone; // disable hinting if the glyphs are transformed + + lockFace(); + FT_Matrix m = this->matrix; + FT_Matrix ftMatrix = glyphSet != 0 ? glyphSet->transformationMatrix : QTransformToFTMatrix(t); + FT_Matrix_Multiply(&ftMatrix, &m); + freetype->matrix = m; + glyph = loadGlyph(glyphSet, g, subPixelPosition, format, false, disableOutlineDrawing); + unlockFace(); + } + + return glyph; +} + +QImage QFontEngineFT::alphaMapForGlyph(glyph_t g, QFixed subPixelPosition) +{ + return alphaMapForGlyph(g, subPixelPosition, QTransform()); +} + +QImage QFontEngineFT::alphaMapForGlyph(glyph_t g, QFixed subPixelPosition, const QTransform &t) +{ + const GlyphFormat neededFormat = antialias ? Format_A8 : Format_Mono; + + Glyph *glyph = loadGlyphFor(g, subPixelPosition, neededFormat, t, false, true); + + QImage img = alphaMapFromGlyphData(glyph, neededFormat); + img = img.copy(); + + if (!cacheEnabled && glyph != &emptyGlyph) + delete glyph; + + if (!img.isNull()) + return img; + + return QFontEngine::alphaMapForGlyph(g, subPixelPosition, t); +} + +QImage QFontEngineFT::alphaRGBMapForGlyph(glyph_t g, QFixed subPixelPosition, const QTransform &t) +{ + if (t.type() > QTransform::TxRotate) + return QFontEngine::alphaRGBMapForGlyph(g, subPixelPosition, t); + + const GlyphFormat neededFormat = Format_A32; + + Glyph *glyph = loadGlyphFor(g, subPixelPosition, neededFormat, t, false, true); + + QImage img = alphaMapFromGlyphData(glyph, neededFormat); + img = img.copy(); + + if (!cacheEnabled && glyph != &emptyGlyph) + delete glyph; + + if (!img.isNull()) + return img; + + return QFontEngine::alphaRGBMapForGlyph(g, subPixelPosition, t); +} + +QImage QFontEngineFT::bitmapForGlyph(glyph_t g, QFixed subPixelPosition, const QTransform &t) +{ + Glyph *glyph = loadGlyphFor(g, subPixelPosition, defaultFormat, t); + if (glyph == Q_NULLPTR) + return QImage(); + + QImage img; + if (defaultFormat == GlyphFormat::Format_ARGB) + img = QImage(glyph->data, glyph->width, glyph->height, QImage::Format_ARGB32_Premultiplied).copy(); + else if (defaultFormat == GlyphFormat::Format_Mono) + img = QImage(glyph->data, glyph->width, glyph->height, QImage::Format_Mono).copy(); + + if (!img.isNull() && (!t.isIdentity() || scalableBitmapScaleFactor != 1)) { + QTransform trans(t); + const qreal scaleFactor = scalableBitmapScaleFactor.toReal(); + trans.scale(scaleFactor, scaleFactor); + img = img.transformed(trans, Qt::SmoothTransformation); + } + + if (!cacheEnabled && glyph != &emptyGlyph) + delete glyph; + + return img; +} + +void QFontEngineFT::removeGlyphFromCache(glyph_t glyph) +{ + defaultGlyphSet.removeGlyphFromCache(glyph, 0); +} + +int QFontEngineFT::glyphCount() const +{ + int count = 0; + FT_Face face = lockFace(); + if (face) { + count = face->num_glyphs; + unlockFace(); + } + return count; +} + +FT_Face QFontEngineFT::lockFace(Scaling scale) const +{ + freetype->lock(); + FT_Face face = freetype->face; + if (scale == Unscaled) { + if (FT_Set_Char_Size(face, face->units_per_EM << 6, face->units_per_EM << 6, 0, 0) == 0) { + freetype->xsize = face->units_per_EM << 6; + freetype->ysize = face->units_per_EM << 6; + } + } else if (freetype->xsize != xsize || freetype->ysize != ysize) { + FT_Set_Char_Size(face, xsize, ysize, 0, 0); + freetype->xsize = xsize; + freetype->ysize = ysize; + } + if (freetype->matrix.xx != matrix.xx || + freetype->matrix.yy != matrix.yy || + freetype->matrix.xy != matrix.xy || + freetype->matrix.yx != matrix.yx) { + freetype->matrix = matrix; + FT_Set_Transform(face, &freetype->matrix, 0); + } + + return face; +} + +void QFontEngineFT::unlockFace() const +{ + freetype->unlock(); +} + +FT_Face QFontEngineFT::non_locked_face() const +{ + return freetype->face; +} + + +QFontEngineFT::QGlyphSet::QGlyphSet() + : outline_drawing(false) +{ + transformationMatrix.xx = 0x10000; + transformationMatrix.yy = 0x10000; + transformationMatrix.xy = 0; + transformationMatrix.yx = 0; + memset(fast_glyph_data, 0, sizeof(fast_glyph_data)); + fast_glyph_count = 0; +} + +QFontEngineFT::QGlyphSet::~QGlyphSet() +{ + clear(); +} + +void QFontEngineFT::QGlyphSet::clear() +{ + if (fast_glyph_count > 0) { + for (int i = 0; i < 256; ++i) { + if (fast_glyph_data[i]) { + delete fast_glyph_data[i]; + fast_glyph_data[i] = 0; + } + } + fast_glyph_count = 0; + } + qDeleteAll(glyph_data); + glyph_data.clear(); +} + +void QFontEngineFT::QGlyphSet::removeGlyphFromCache(glyph_t index, QFixed subPixelPosition) +{ + if (useFastGlyphData(index, subPixelPosition)) { + if (fast_glyph_data[index]) { + delete fast_glyph_data[index]; + fast_glyph_data[index] = 0; + if (fast_glyph_count > 0) + --fast_glyph_count; + } + } else { + delete glyph_data.take(GlyphAndSubPixelPosition(index, subPixelPosition)); + } +} + +void QFontEngineFT::QGlyphSet::setGlyph(glyph_t index, QFixed subPixelPosition, Glyph *glyph) +{ + if (useFastGlyphData(index, subPixelPosition)) { + if (!fast_glyph_data[index]) + ++fast_glyph_count; + fast_glyph_data[index] = glyph; + } else { + glyph_data.insert(GlyphAndSubPixelPosition(index, subPixelPosition), glyph); + } +} + +int QFontEngineFT::getPointInOutline(glyph_t glyph, int flags, quint32 point, QFixed *xpos, QFixed *ypos, quint32 *nPoints) +{ + lockFace(); + bool hsubpixel = true; + int vfactor = 1; + int load_flags = loadFlags(0, Format_A8, flags, hsubpixel, vfactor); + int result = freetype->getPointInOutline(glyph, load_flags, point, xpos, ypos, nPoints); + unlockFace(); + return result; +} + +bool QFontEngineFT::initFromFontEngine(const QFontEngineFT *fe) +{ + if (!init(fe->faceId(), fe->antialias, fe->defaultFormat, fe->freetype)) + return false; + + // Increase the reference of this QFreetypeFace since one more QFontEngineFT + // will be using it + freetype->ref.ref(); + + default_load_flags = fe->default_load_flags; + default_hint_style = fe->default_hint_style; + antialias = fe->antialias; + transform = fe->transform; + embolden = fe->embolden; + obliquen = fe->obliquen; + subpixelType = fe->subpixelType; + lcdFilterType = fe->lcdFilterType; + embeddedbitmap = fe->embeddedbitmap; + + return true; +} + +QFontEngine *QFontEngineFT::cloneWithSize(qreal pixelSize) const +{ + QFontDef fontDef(this->fontDef); + fontDef.pixelSize = pixelSize; + QFontEngineFT *fe = new QFontEngineFT(fontDef); + if (!fe->initFromFontEngine(this)) { + delete fe; + return 0; + } else { + return fe; + } +} + +Qt::HANDLE QFontEngineFT::handle() const +{ + return non_locked_face(); +} + +QT_END_NAMESPACE + +#endif // QT_NO_FREETYPE diff --git a/src/platformsupport/fontdatabases/basic/qfontengine_ft_p.h b/src/platformsupport/fontdatabases/basic/qfontengine_ft_p.h new file mode 100644 index 0000000000..c5f3b0443e --- /dev/null +++ b/src/platformsupport/fontdatabases/basic/qfontengine_ft_p.h @@ -0,0 +1,358 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QFONTENGINE_FT_P_H +#define QFONTENGINE_FT_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "private/qfontengine_p.h" + +#ifndef QT_NO_FREETYPE + +#include +#include FT_FREETYPE_H + + +#ifndef Q_OS_WIN +#include +#endif + +#include + +QT_BEGIN_NAMESPACE + +class QFontEngineFTRawFont; +class QFontconfigDatabase; + +/* + * This class represents one font file on disk (like Arial.ttf) and is shared between all the font engines + * that show this font file (at different pixel sizes). + */ +class QFreetypeFace +{ +public: + void computeSize(const QFontDef &fontDef, int *xsize, int *ysize, bool *outline_drawing, QFixed *scalableBitmapScaleFactor); + QFontEngine::Properties properties() const; + bool getSfntTable(uint tag, uchar *buffer, uint *length) const; + + static QFreetypeFace *getFace(const QFontEngine::FaceId &face_id, + const QByteArray &fontData = QByteArray()); + void release(const QFontEngine::FaceId &face_id); + + // locks the struct for usage. Any read/write operations require locking. + void lock() + { + _lock.lock(); + } + void unlock() + { + _lock.unlock(); + } + + FT_Face face; + int xsize; // 26.6 + int ysize; // 26.6 + FT_Matrix matrix; + FT_CharMap unicode_map; + FT_CharMap symbol_map; + + enum { cmapCacheSize = 0x200 }; + glyph_t cmapCache[cmapCacheSize]; + + int fsType() const; + + int getPointInOutline(glyph_t glyph, int flags, quint32 point, QFixed *xpos, QFixed *ypos, quint32 *nPoints); + + bool isScalableBitmap() const; + + static void addGlyphToPath(FT_Face face, FT_GlyphSlot g, const QFixedPoint &point, QPainterPath *path, FT_Fixed x_scale, FT_Fixed y_scale); + static void addBitmapToPath(FT_GlyphSlot slot, const QFixedPoint &point, QPainterPath *path); + +private: + friend class QFontEngineFT; + friend class QtFreetypeData; + friend struct QScopedPointerDeleter; + QFreetypeFace() : _lock(QMutex::Recursive) {} + ~QFreetypeFace() {} + void cleanup(); + QAtomicInt ref; + QMutex _lock; + QByteArray fontData; + + QFontEngine::Holder hbFace; +}; + +class QFontEngineFT : public QFontEngine +{ +public: + + /* we don't cache glyphs that are too large anyway, so we can make this struct rather small */ + struct Glyph { + ~Glyph(); + short linearAdvance; + unsigned char width; + unsigned char height; + short x; + short y; + short advance; + signed char format; + uchar *data; + }; + + struct GlyphInfo { + int linearAdvance; + unsigned short width; + unsigned short height; + short x; + short y; + short xOff; + short yOff; + }; + + struct GlyphAndSubPixelPosition + { + GlyphAndSubPixelPosition(glyph_t g, QFixed spp) : glyph(g), subPixelPosition(spp) {} + + bool operator==(const GlyphAndSubPixelPosition &other) const + { + return glyph == other.glyph && subPixelPosition == other.subPixelPosition; + } + + glyph_t glyph; + QFixed subPixelPosition; + }; + + struct QGlyphSet + { + QGlyphSet(); + ~QGlyphSet(); + FT_Matrix transformationMatrix; + bool outline_drawing; + + void removeGlyphFromCache(glyph_t index, QFixed subPixelPosition); + void clear(); + inline bool useFastGlyphData(glyph_t index, QFixed subPixelPosition) const { + return (index < 256 && subPixelPosition == 0); + } + inline Glyph *getGlyph(glyph_t index, QFixed subPixelPosition = 0) const; + void setGlyph(glyph_t index, QFixed spp, Glyph *glyph); + + inline bool isGlyphMissing(glyph_t index) const { return missing_glyphs.contains(index); } + inline void setGlyphMissing(glyph_t index) const { missing_glyphs.insert(index); } +private: + mutable QHash glyph_data; // maps from glyph index to glyph data + mutable QSet missing_glyphs; + mutable Glyph *fast_glyph_data[256]; // for fast lookup of glyphs < 256 + mutable int fast_glyph_count; + }; + + QFontEngine::FaceId faceId() const Q_DECL_OVERRIDE; + QFontEngine::Properties properties() const Q_DECL_OVERRIDE; + QFixed emSquareSize() const Q_DECL_OVERRIDE; + bool supportsSubPixelPositions() const Q_DECL_OVERRIDE + { + return default_hint_style == HintLight || + default_hint_style == HintNone; + } + + bool getSfntTableData(uint tag, uchar *buffer, uint *length) const Q_DECL_OVERRIDE; + int synthesized() const Q_DECL_OVERRIDE; + + QFixed ascent() const Q_DECL_OVERRIDE; + QFixed capHeight() const Q_DECL_OVERRIDE; + QFixed descent() const Q_DECL_OVERRIDE; + QFixed leading() const Q_DECL_OVERRIDE; + QFixed xHeight() const Q_DECL_OVERRIDE; + QFixed averageCharWidth() const Q_DECL_OVERRIDE; + + qreal maxCharWidth() const Q_DECL_OVERRIDE; + QFixed lineThickness() const Q_DECL_OVERRIDE; + QFixed underlinePosition() const Q_DECL_OVERRIDE; + + glyph_t glyphIndex(uint ucs4) const Q_DECL_OVERRIDE; + void doKerning(QGlyphLayout *, ShaperFlags) const Q_DECL_OVERRIDE; + + void getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics) Q_DECL_OVERRIDE; + + bool supportsTransformation(const QTransform &transform) const Q_DECL_OVERRIDE; + + void addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs, + QPainterPath *path, QTextItem::RenderFlags flags) Q_DECL_OVERRIDE; + void addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, + QPainterPath *path, QTextItem::RenderFlags flags) Q_DECL_OVERRIDE; + + bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const Q_DECL_OVERRIDE; + + glyph_metrics_t boundingBox(const QGlyphLayout &glyphs) Q_DECL_OVERRIDE; + glyph_metrics_t boundingBox(glyph_t glyph) Q_DECL_OVERRIDE; + glyph_metrics_t boundingBox(glyph_t glyph, const QTransform &matrix) Q_DECL_OVERRIDE; + + void recalcAdvances(QGlyphLayout *glyphs, ShaperFlags flags) const Q_DECL_OVERRIDE; + QImage alphaMapForGlyph(glyph_t g) Q_DECL_OVERRIDE { return alphaMapForGlyph(g, 0); } + QImage alphaMapForGlyph(glyph_t, QFixed) Q_DECL_OVERRIDE; + QImage alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition, const QTransform &t) Q_DECL_OVERRIDE; + QImage alphaRGBMapForGlyph(glyph_t, QFixed subPixelPosition, const QTransform &t) Q_DECL_OVERRIDE; + QImage bitmapForGlyph(glyph_t, QFixed subPixelPosition, const QTransform &t) Q_DECL_OVERRIDE; + glyph_metrics_t alphaMapBoundingBox(glyph_t glyph, + QFixed subPixelPosition, + const QTransform &matrix, + QFontEngine::GlyphFormat format) Q_DECL_OVERRIDE; + QImage *lockedAlphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition, + GlyphFormat neededFormat, const QTransform &t, + QPoint *offset) Q_DECL_OVERRIDE; + bool hasInternalCaching() const Q_DECL_OVERRIDE { return cacheEnabled; } + void unlockAlphaMapForGlyph() Q_DECL_OVERRIDE; + + void removeGlyphFromCache(glyph_t glyph) Q_DECL_OVERRIDE; + int glyphMargin(QFontEngine::GlyphFormat /* format */) Q_DECL_OVERRIDE { return 0; } + + int glyphCount() const Q_DECL_OVERRIDE; + + enum Scaling { + Scaled, + Unscaled + }; + FT_Face lockFace(Scaling scale = Scaled) const; + void unlockFace() const; + + FT_Face non_locked_face() const; + + inline bool drawAntialiased() const { return antialias; } + inline bool invalid() const { return xsize == 0 && ysize == 0; } + inline bool isBitmapFont() const { return defaultFormat == Format_Mono; } + inline bool isScalableBitmap() const { return freetype->isScalableBitmap(); } + + inline Glyph *loadGlyph(uint glyph, QFixed subPixelPosition, GlyphFormat format = Format_None, bool fetchMetricsOnly = false, bool disableOutlineDrawing = false) const + { return loadGlyph(cacheEnabled ? &defaultGlyphSet : 0, glyph, subPixelPosition, format, fetchMetricsOnly, disableOutlineDrawing); } + Glyph *loadGlyph(QGlyphSet *set, uint glyph, QFixed subPixelPosition, GlyphFormat = Format_None, bool fetchMetricsOnly = false, bool disableOutlineDrawing = false) const; + Glyph *loadGlyphFor(glyph_t g, QFixed subPixelPosition, GlyphFormat format, const QTransform &t, bool fetchBoundingBox = false, bool disableOutlineDrawing = false); + + QGlyphSet *loadGlyphSet(const QTransform &matrix); + + QFontEngineFT(const QFontDef &fd); + virtual ~QFontEngineFT(); + + bool init(FaceId faceId, bool antiaalias, GlyphFormat defaultFormat = Format_None, + const QByteArray &fontData = QByteArray()); + bool init(FaceId faceId, bool antialias, GlyphFormat format, + QFreetypeFace *freetypeFace); + + int getPointInOutline(glyph_t glyph, int flags, quint32 point, QFixed *xpos, QFixed *ypos, quint32 *nPoints) Q_DECL_OVERRIDE; + + void setQtDefaultHintStyle(QFont::HintingPreference hintingPreference); + void setDefaultHintStyle(HintStyle style) Q_DECL_OVERRIDE; + + QFontEngine *cloneWithSize(qreal pixelSize) const Q_DECL_OVERRIDE; + Qt::HANDLE handle() const Q_DECL_OVERRIDE; + bool initFromFontEngine(const QFontEngineFT *fontEngine); + + HintStyle defaultHintStyle() const { return default_hint_style; } +protected: + + QFreetypeFace *freetype; + mutable int default_load_flags; + HintStyle default_hint_style; + bool antialias; + bool transform; + bool embolden; + bool obliquen; + SubpixelAntialiasingType subpixelType; + int lcdFilterType; + bool embeddedbitmap; + bool cacheEnabled; + bool forceAutoHint; + +private: + friend class QFontEngineFTRawFont; + friend class QFontconfigDatabase; + friend class QBasicFontDatabase; + friend class QCoreTextFontDatabase; + friend class QFontEngineMultiFontConfig; + + int loadFlags(QGlyphSet *set, GlyphFormat format, int flags, bool &hsubpixel, int &vfactor) const; + bool shouldUseDesignMetrics(ShaperFlags flags) const; + QFixed scaledBitmapMetrics(QFixed m) const; + glyph_metrics_t scaledBitmapMetrics(const glyph_metrics_t &m) const; + + GlyphFormat defaultFormat; + FT_Matrix matrix; + + QList transformedGlyphSets; + mutable QGlyphSet defaultGlyphSet; + + QFontEngine::FaceId face_id; + + int xsize; + int ysize; + + QFixed line_thickness; + QFixed underline_position; + + FT_Size_Metrics metrics; + mutable bool kerning_pairs_loaded; + QFixed scalableBitmapScaleFactor; +}; + +inline uint qHash(const QFontEngineFT::GlyphAndSubPixelPosition &g) +{ + return (g.glyph << 8) | (g.subPixelPosition * 10).round().toInt(); +} + +inline QFontEngineFT::Glyph *QFontEngineFT::QGlyphSet::getGlyph(glyph_t index, QFixed subPixelPosition) const +{ + if (useFastGlyphData(index, subPixelPosition)) + return fast_glyph_data[index]; + return glyph_data.value(GlyphAndSubPixelPosition(index, subPixelPosition)); +} + +extern FT_Library qt_getFreetype(); + +QT_END_NAMESPACE + +#endif // QT_NO_FREETYPE + +#endif // QFONTENGINE_FT_P_H diff --git a/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp b/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp index 65bc494e91..2c5ce3e87d 100644 --- a/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp +++ b/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp @@ -40,6 +40,8 @@ #include "qfontconfigdatabase_p.h" #include "qfontenginemultifontconfig_p.h" +#include + #include #include #include @@ -49,7 +51,6 @@ #include #include -#include #include #include diff --git a/src/platformsupport/fontdatabases/fontconfig/qfontenginemultifontconfig.cpp b/src/platformsupport/fontdatabases/fontconfig/qfontenginemultifontconfig.cpp index 7574f9f9e6..2fbcb6216e 100644 --- a/src/platformsupport/fontdatabases/fontconfig/qfontenginemultifontconfig.cpp +++ b/src/platformsupport/fontdatabases/fontconfig/qfontenginemultifontconfig.cpp @@ -39,7 +39,7 @@ #include "qfontenginemultifontconfig_p.h" -#include +#include QT_BEGIN_NAMESPACE diff --git a/src/platformsupport/fontdatabases/mac/coretext.pri b/src/platformsupport/fontdatabases/mac/coretext.pri index 1caeb2c1ac..f4e9404371 100644 --- a/src/platformsupport/fontdatabases/mac/coretext.pri +++ b/src/platformsupport/fontdatabases/mac/coretext.pri @@ -3,8 +3,8 @@ OBJECTIVE_SOURCES += $$PWD/qfontengine_coretext.mm $$PWD/qcoretextfontdatabase.m qtConfig(freetype) { QMAKE_USE += freetype - HEADERS += $$QT_SOURCE_TREE/src/gui/text/qfontengine_ft_p.h - SOURCES += $$QT_SOURCE_TREE/src/gui/text/qfontengine_ft.cpp + HEADERS += basic/qfontengine_ft_p.h + SOURCES += basic/qfontengine_ft.cpp } uikit: \ diff --git a/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm b/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm index 1e29b12ec4..3d94982f60 100644 --- a/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm +++ b/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm @@ -53,7 +53,7 @@ #include #include #ifndef QT_NO_FREETYPE -#include +#include #endif QT_BEGIN_NAMESPACE diff --git a/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase_ft.cpp b/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase_ft.cpp index 7cfebf0436..65947ab7da 100644 --- a/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase_ft.cpp +++ b/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase_ft.cpp @@ -40,6 +40,8 @@ #include "qwindowsfontdatabase_ft_p.h" #include "qwindowsfontdatabase_p.h" +#include + #include #include FT_TRUETYPE_TABLES_H @@ -47,7 +49,6 @@ #include #include #include -#include #include #include diff --git a/src/platformsupport/fontdatabases/winrt/qwinrtfontdatabase.cpp b/src/platformsupport/fontdatabases/winrt/qwinrtfontdatabase.cpp index 9f4b182ece..eb5a38855e 100644 --- a/src/platformsupport/fontdatabases/winrt/qwinrtfontdatabase.cpp +++ b/src/platformsupport/fontdatabases/winrt/qwinrtfontdatabase.cpp @@ -39,11 +39,12 @@ #include "qwinrtfontdatabase_p.h" +#include + #include #include #include -#include #include #include using namespace Microsoft::WRL; -- cgit v1.2.3 From 5152c3ca82a0f47273e3bfc59a5a29e0503ed7dd Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Wed, 2 Nov 2016 16:29:51 +0100 Subject: make freetype & fontconfig dependencies private again exporting the freetype dependency caused qtwayland to break with -qt-freetype, as the helper libraries' module pri files are not installed (for good reasons) after f9a80e06a, no actual user of FontDatabaseSupport needs access to the transitive dependencies anyway (one of the headers has a fontconfig dependency, but it's not used outside the module itself), so hiding them again is just fine. this partially reverts ec774500f. Task-number: QTBUG-56666 Change-Id: I9e68a7e0725a92833b856c9ffdbec61c8aa5fed2 Reviewed-by: Joerg Bornemann --- src/platformsupport/fontdatabases/basic/basic.pri | 2 +- src/platformsupport/fontdatabases/fontconfig/fontconfig.pri | 2 +- src/platformsupport/fontdatabases/mac/coretext.pri | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/platformsupport/fontdatabases/basic/basic.pri b/src/platformsupport/fontdatabases/basic/basic.pri index d16aabb4c7..0617bf74d7 100644 --- a/src/platformsupport/fontdatabases/basic/basic.pri +++ b/src/platformsupport/fontdatabases/basic/basic.pri @@ -6,4 +6,4 @@ SOURCES += \ $$PWD/qbasicfontdatabase.cpp \ $$PWD/qfontengine_ft.cpp -QMAKE_USE += freetype +QMAKE_USE_PRIVATE += freetype diff --git a/src/platformsupport/fontdatabases/fontconfig/fontconfig.pri b/src/platformsupport/fontdatabases/fontconfig/fontconfig.pri index 6458464870..671d6be237 100644 --- a/src/platformsupport/fontdatabases/fontconfig/fontconfig.pri +++ b/src/platformsupport/fontdatabases/fontconfig/fontconfig.pri @@ -3,4 +3,4 @@ HEADERS += $$PWD/qfontconfigdatabase_p.h \ SOURCES += $$PWD/qfontconfigdatabase.cpp \ $$PWD/qfontenginemultifontconfig.cpp -QMAKE_USE += fontconfig +QMAKE_USE_PRIVATE += fontconfig diff --git a/src/platformsupport/fontdatabases/mac/coretext.pri b/src/platformsupport/fontdatabases/mac/coretext.pri index f4e9404371..50dafc3f89 100644 --- a/src/platformsupport/fontdatabases/mac/coretext.pri +++ b/src/platformsupport/fontdatabases/mac/coretext.pri @@ -2,7 +2,7 @@ HEADERS += $$PWD/qcoretextfontdatabase_p.h $$PWD/qfontengine_coretext_p.h OBJECTIVE_SOURCES += $$PWD/qfontengine_coretext.mm $$PWD/qcoretextfontdatabase.mm qtConfig(freetype) { - QMAKE_USE += freetype + QMAKE_USE_PRIVATE += freetype HEADERS += basic/qfontengine_ft_p.h SOURCES += basic/qfontengine_ft.cpp } -- cgit v1.2.3 From 9e579f64d7cb1bb6c185ff5555b3e4b4b2076dea Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Thu, 2 Feb 2017 20:10:23 +0100 Subject: don't litter configure test build dirs with .qmake.cache files there should be only one, at the level of the isolating .qmake.conf. Change-Id: I25f05864d6f5c1bb5caf2fb4138adb4bb9cc2f22 Reviewed-by: Friedemann Kleint Reviewed-by: Oswald Buddenhagen --- mkspecs/features/qt_configure.prf | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mkspecs/features/qt_configure.prf b/mkspecs/features/qt_configure.prf index 81c63205a7..2732d2ad0f 100644 --- a/mkspecs/features/qt_configure.prf +++ b/mkspecs/features/qt_configure.prf @@ -718,7 +718,8 @@ defineTest(qtConfTest_compile) { isEmpty(host): host = false test_dir = $$QMAKE_CONFIG_TESTS_DIR/$$test - test_out_dir = $$shadowed($$test_dir) + test_base_out_dir = $$shadowed($$QMAKE_CONFIG_TESTS_DIR) + test_out_dir = $$test_base_out_dir/$$test !isEmpty($${1}.pro): \ test_dir = $$test_dir/$$eval($${1}.pro) test_cmd_base = "$$QMAKE_CD $$system_quote($$system_path($$test_out_dir)) &&" @@ -775,7 +776,7 @@ defineTest(qtConfTest_compile) { QMAKE_MAKE = "$$QMAKE_MAKE clean && $$QMAKE_MAKE" mkpath($$test_out_dir)|error() - write_file($$test_out_dir/.qmake.cache)|error() + write_file($$test_base_out_dir/.qmake.cache)|error() # add possible command line args qmake_args += $$qtConfPrepareArgs($$eval($${1}.args)) $$eval($${1}.literal_args) -- cgit v1.2.3 From ba43ecade3d86673c4b71f3a048b88330285cfe0 Mon Sep 17 00:00:00 2001 From: Julien Gueytat Date: Fri, 3 Feb 2017 01:36:36 +0100 Subject: Move configuration into includes Use of gcc-base.conf, g++-base.conf files and creation of solaris.conf. The content of solaris.conf should follow the content of linux.conf. Task-number: QTBUG-56293 Change-Id: I59cf9efa82ab0a2b22ea1a58f6339280460e5f92 Reviewed-by: Oswald Buddenhagen --- mkspecs/common/solaris.conf | 27 +++++++++++++++++ mkspecs/solaris-g++-64/qmake.conf | 62 +++++++-------------------------------- mkspecs/solaris-g++/qmake.conf | 61 +++++--------------------------------- 3 files changed, 45 insertions(+), 105 deletions(-) create mode 100644 mkspecs/common/solaris.conf diff --git a/mkspecs/common/solaris.conf b/mkspecs/common/solaris.conf new file mode 100644 index 0000000000..d965cb44d0 --- /dev/null +++ b/mkspecs/common/solaris.conf @@ -0,0 +1,27 @@ +# +# qmake configuration for common solaris +# + +QMAKE_PLATFORM += solaris + +include(unix.conf) + +QMAKE_CFLAGS_THREAD = -D_REENTRANT +QMAKE_CXXFLAGS_THREAD = $$QMAKE_CFLAGS_THREAD + +QMAKE_INCDIR = /usr/sfw/include +QMAKE_INCDIR_X11 = /usr/openwin/include +QMAKE_INCDIR_OPENGL = /usr/openwin/include + +QMAKE_LIBS = +QMAKE_LIBS_DYNLOAD = -ldl +QMAKE_LIBS_X11 = -lXext -lX11 -lresolv -lsocket -lnsl +QMAKE_LIBS_NIS = +QMAKE_LIBS_OPENGL = -lGL +QMAKE_LIBS_THREAD = -lpthread -lrt +QMAKE_LIBS_NETWORK = -lresolv -lsocket -lxnet -lnsl + +QMAKE_AR = ar cq +QMAKE_OBJCOPY = objcopy +QMAKE_NM = nm -P +QMAKE_RANLIB = diff --git a/mkspecs/solaris-g++-64/qmake.conf b/mkspecs/solaris-g++-64/qmake.conf index 91ffb9193e..e42dc970de 100644 --- a/mkspecs/solaris-g++-64/qmake.conf +++ b/mkspecs/solaris-g++-64/qmake.conf @@ -1,5 +1,5 @@ # -# qmake configuration for solaris-g++64 +# qmake configuration for solaris-g++-64 # # The X11 header files used to be broken on Solaris until patches were # released in early 2001 for Solaris 2.6, 7, and 8. On Solaris 2.5.1 @@ -26,54 +26,20 @@ # MAKEFILE_GENERATOR = UNIX -QMAKE_PLATFORM = solaris +CONFIG += incremental +QMAKE_INCREMENTAL_STYLE = sublib -include(../common/unix.conf) +include(../common/solaris.conf) -QMAKE_COMPILER = gcc - -QMAKE_CC = gcc -QMAKE_LEX = flex -QMAKE_LEXFLAGS = -QMAKE_YACC = yacc -QMAKE_YACCFLAGS = -d QMAKE_CFLAGS = -m64 -D_XOPEN_SOURCE=500 -D__EXTENSIONS__ -QMAKE_CFLAGS_DEPS = -M -QMAKE_CFLAGS_WARN_ON = -Wall -W -QMAKE_CFLAGS_WARN_OFF = -w -QMAKE_CFLAGS_RELEASE = -O2 -QMAKE_CFLAGS_DEBUG = -g -QMAKE_CFLAGS_SHLIB = -fPIC -QMAKE_CFLAGS_STATIC_LIB = $$QMAKE_CFLAGS_SHLIB -QMAKE_CFLAGS_YACC = -Wno-unused -Wno-parentheses -QMAKE_CFLAGS_THREAD = -D_REENTRANT - -QMAKE_CXX = g++ -QMAKE_CXXFLAGS = $$QMAKE_CFLAGS -QMAKE_CXXFLAGS_DEPS = $$QMAKE_CFLAGS_DEPS -QMAKE_CXXFLAGS_WARN_ON = $$QMAKE_CFLAGS_WARN_ON -QMAKE_CXXFLAGS_WARN_OFF = $$QMAKE_CFLAGS_WARN_OFF -QMAKE_CXXFLAGS_RELEASE = $$QMAKE_CFLAGS_RELEASE -QMAKE_CXXFLAGS_DEBUG = $$QMAKE_CFLAGS_DEBUG -QMAKE_CXXFLAGS_SHLIB = $$QMAKE_CFLAGS_SHLIB -QMAKE_CXXFLAGS_STATIC_LIB = $$QMAKE_CFLAGS_STATIC_LIB -QMAKE_CXXFLAGS_YACC = $$QMAKE_CFLAGS_YACC -QMAKE_CXXFLAGS_THREAD = $$QMAKE_CFLAGS_THREAD +QMAKE_LFLAGS = -m64 -QMAKE_INCDIR = /usr/sfw/include QMAKE_LIBDIR = /usr/sfw/lib/64 -QMAKE_INCDIR_X11 = /usr/openwin/include QMAKE_LIBDIR_X11 = /usr/openwin/lib/64 -QMAKE_INCDIR_OPENGL = /usr/openwin/include QMAKE_LIBDIR_OPENGL = /usr/openwin/lib/64 -QMAKE_LINK = g++ -QMAKE_LINK_SHLIB = g++ -QMAKE_LINK_C = gcc -QMAKE_LINK_C_SHLIB = gcc -QMAKE_LFLAGS = -m64 -QMAKE_LFLAGS_RELEASE = -QMAKE_LFLAGS_DEBUG = -g +include(../common/gcc-base.conf) + QMAKE_LFLAGS_SHLIB = -shared QMAKE_LFLAGS_PLUGIN = $$QMAKE_LFLAGS_SHLIB QMAKE_LFLAGS_SONAME = -h$$LITERAL_WHITESPACE @@ -81,17 +47,9 @@ QMAKE_LFLAGS_THREAD = QMAKE_LFLAGS_NOUNDEF = -z defs QMAKE_LFLAGS_RPATH = -Wl,-R, -QMAKE_LIBS = -QMAKE_LIBS_DYNLOAD = -ldl -QMAKE_LIBS_X11 = -lXext -lX11 -lresolv -lsocket -lnsl -QMAKE_LIBS_NIS = -QMAKE_LIBS_OPENGL = -lGL -QMAKE_LIBS_THREAD = -lpthread -lrt -QMAKE_LIBS_NETWORK = -lresolv -lsocket -lxnet -lnsl +include(../common/g++-base.conf) -QMAKE_AR = ar cq -QMAKE_OBJCOPY = objcopy -QMAKE_NM = nm -P -QMAKE_RANLIB = +QMAKE_LFLAGS_RELEASE = +QMAKE_LFLAGS_DEBUG = -g load(qt_config) diff --git a/mkspecs/solaris-g++/qmake.conf b/mkspecs/solaris-g++/qmake.conf index 594646353d..6f7d9e4c05 100644 --- a/mkspecs/solaris-g++/qmake.conf +++ b/mkspecs/solaris-g++/qmake.conf @@ -9,54 +9,17 @@ # MAKEFILE_GENERATOR = UNIX -QMAKE_PLATFORM = solaris +CONFIG += incremental +QMAKE_INCREMENTAL_STYLE = sublib -include(../common/unix.conf) +include(../common/solaris.conf) -QMAKE_COMPILER = gcc - -QMAKE_CC = gcc -QMAKE_LEX = flex -QMAKE_LEXFLAGS = -QMAKE_YACC = yacc -QMAKE_YACCFLAGS = -d -QMAKE_CFLAGS = -QMAKE_CFLAGS_DEPS = -M -QMAKE_CFLAGS_WARN_ON = -Wall -W -QMAKE_CFLAGS_WARN_OFF = -w -QMAKE_CFLAGS_RELEASE = -O2 -QMAKE_CFLAGS_DEBUG = -g -QMAKE_CFLAGS_SHLIB = -fPIC -QMAKE_CFLAGS_STATIC_LIB = $$QMAKE_CFLAGS_SHLIB -QMAKE_CFLAGS_YACC = -Wno-unused -Wno-parentheses -QMAKE_CFLAGS_THREAD = -D_REENTRANT - -QMAKE_CXX = g++ -QMAKE_CXXFLAGS = $$QMAKE_CFLAGS -QMAKE_CXXFLAGS_DEPS = $$QMAKE_CFLAGS_DEPS -QMAKE_CXXFLAGS_WARN_ON = $$QMAKE_CFLAGS_WARN_ON -QMAKE_CXXFLAGS_WARN_OFF = $$QMAKE_CFLAGS_WARN_OFF -QMAKE_CXXFLAGS_RELEASE = $$QMAKE_CFLAGS_RELEASE -QMAKE_CXXFLAGS_DEBUG = $$QMAKE_CFLAGS_DEBUG -QMAKE_CXXFLAGS_SHLIB = $$QMAKE_CFLAGS_SHLIB -QMAKE_CXXFLAGS_STATIC_LIB = $$QMAKE_CFLAGS_STATIC_LIB -QMAKE_CXXFLAGS_YACC = $$QMAKE_CFLAGS_YACC -QMAKE_CXXFLAGS_THREAD = $$QMAKE_CFLAGS_THREAD - -QMAKE_INCDIR = /usr/sfw/include QMAKE_LIBDIR = /usr/sfw/lib -QMAKE_INCDIR_X11 = /usr/openwin/include QMAKE_LIBDIR_X11 = /usr/openwin/lib -QMAKE_INCDIR_OPENGL = /usr/openwin/include QMAKE_LIBDIR_OPENGL = /usr/openwin/lib -QMAKE_LINK = g++ -QMAKE_LINK_SHLIB = g++ -QMAKE_LINK_C = gcc -QMAKE_LINK_C_SHLIB = gcc -QMAKE_LFLAGS = -QMAKE_LFLAGS_RELEASE = -QMAKE_LFLAGS_DEBUG = -g +include(../common/gcc-base.conf) + QMAKE_LFLAGS_SHLIB = -shared QMAKE_LFLAGS_PLUGIN = $$QMAKE_LFLAGS_SHLIB QMAKE_LFLAGS_SONAME = -h$$LITERAL_WHITESPACE @@ -64,17 +27,9 @@ QMAKE_LFLAGS_THREAD = QMAKE_LFLAGS_NOUNDEF = -z defs QMAKE_LFLAGS_RPATH = -Wl,-R, -QMAKE_LIBS = -QMAKE_LIBS_DYNLOAD = -ldl -QMAKE_LIBS_X11 = -lXext -lX11 -lresolv -lsocket -lnsl -QMAKE_LIBS_NIS = -QMAKE_LIBS_OPENGL = -lGL -QMAKE_LIBS_THREAD = -lpthread -lrt -QMAKE_LIBS_NETWORK = -lresolv -lsocket -lxnet -lnsl +include(../common/g++-base.conf) -QMAKE_AR = ar cq -QMAKE_OBJCOPY = objcopy -QMAKE_NM = nm -P -QMAKE_RANLIB = +QMAKE_LFLAGS_RELEASE = +QMAKE_LFLAGS_DEBUG = -g load(qt_config) -- cgit v1.2.3 From 9d6d9984f56366f40b4e0c5391a2acb988d2b2c7 Mon Sep 17 00:00:00 2001 From: Julien Gueytat Date: Fri, 3 Feb 2017 01:44:20 +0100 Subject: Update Solaris include path and library path The path match OpenIndiana distribution based on Illumos. Task-number: QTBUG-56293 Change-Id: I44e7defa63809dc4f413b46329481b53e5e74c30 Reviewed-by: Oswald Buddenhagen --- mkspecs/common/solaris.conf | 5 ++--- mkspecs/solaris-g++-64/qmake.conf | 5 ++--- mkspecs/solaris-g++/qmake.conf | 5 ++--- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/mkspecs/common/solaris.conf b/mkspecs/common/solaris.conf index d965cb44d0..8581838434 100644 --- a/mkspecs/common/solaris.conf +++ b/mkspecs/common/solaris.conf @@ -9,9 +9,8 @@ include(unix.conf) QMAKE_CFLAGS_THREAD = -D_REENTRANT QMAKE_CXXFLAGS_THREAD = $$QMAKE_CFLAGS_THREAD -QMAKE_INCDIR = /usr/sfw/include -QMAKE_INCDIR_X11 = /usr/openwin/include -QMAKE_INCDIR_OPENGL = /usr/openwin/include +QMAKE_INCDIR_X11 = /usr/X11/include +QMAKE_INCDIR_OPENGL = /usr/X11/include/mesa QMAKE_LIBS = QMAKE_LIBS_DYNLOAD = -ldl diff --git a/mkspecs/solaris-g++-64/qmake.conf b/mkspecs/solaris-g++-64/qmake.conf index e42dc970de..6fc93d45b7 100644 --- a/mkspecs/solaris-g++-64/qmake.conf +++ b/mkspecs/solaris-g++-64/qmake.conf @@ -34,9 +34,8 @@ include(../common/solaris.conf) QMAKE_CFLAGS = -m64 -D_XOPEN_SOURCE=500 -D__EXTENSIONS__ QMAKE_LFLAGS = -m64 -QMAKE_LIBDIR = /usr/sfw/lib/64 -QMAKE_LIBDIR_X11 = /usr/openwin/lib/64 -QMAKE_LIBDIR_OPENGL = /usr/openwin/lib/64 +QMAKE_LIBDIR_X11 = /usr/X11/lib/64 +QMAKE_LIBDIR_OPENGL = /usr/X11/lib/64 include(../common/gcc-base.conf) diff --git a/mkspecs/solaris-g++/qmake.conf b/mkspecs/solaris-g++/qmake.conf index 6f7d9e4c05..943b0865c9 100644 --- a/mkspecs/solaris-g++/qmake.conf +++ b/mkspecs/solaris-g++/qmake.conf @@ -14,9 +14,8 @@ QMAKE_INCREMENTAL_STYLE = sublib include(../common/solaris.conf) -QMAKE_LIBDIR = /usr/sfw/lib -QMAKE_LIBDIR_X11 = /usr/openwin/lib -QMAKE_LIBDIR_OPENGL = /usr/openwin/lib +QMAKE_LIBDIR_X11 = /usr/X11/lib +QMAKE_LIBDIR_OPENGL = /usr/X11/lib include(../common/gcc-base.conf) -- cgit v1.2.3 From 91f8a41404acd33c54021247eb550c6f796f1f61 Mon Sep 17 00:00:00 2001 From: Julien Gueytat Date: Mon, 6 Feb 2017 23:00:53 +0100 Subject: Complete solaris.conf following linux.conf template Task-number: QTBUG-56293 Change-Id: I9e0240b3d4766f5c740a044d6eff44d21b340dc0 Reviewed-by: Oswald Buddenhagen --- mkspecs/common/solaris.conf | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/mkspecs/common/solaris.conf b/mkspecs/common/solaris.conf index 8581838434..b53227204d 100644 --- a/mkspecs/common/solaris.conf +++ b/mkspecs/common/solaris.conf @@ -8,6 +8,10 @@ include(unix.conf) QMAKE_CFLAGS_THREAD = -D_REENTRANT QMAKE_CXXFLAGS_THREAD = $$QMAKE_CFLAGS_THREAD +QMAKE_LFLAGS_GCSECTIONS = -Wl,-z,ignore + +QMAKE_LFLAGS_REL_RPATH = -Wl,-z,origin +QMAKE_REL_RPATH_BASE = $ORIGIN QMAKE_INCDIR_X11 = /usr/X11/include QMAKE_INCDIR_OPENGL = /usr/X11/include/mesa @@ -24,3 +28,6 @@ QMAKE_AR = ar cq QMAKE_OBJCOPY = objcopy QMAKE_NM = nm -P QMAKE_RANLIB = + +QMAKE_STRIP = gstrip +QMAKE_STRIPFLAGS_LIB += --strip-unneeded -- cgit v1.2.3 From 5f8fc8395f73f6a342ba9e41d97322241b280f62 Mon Sep 17 00:00:00 2001 From: Julien Gueytat Date: Sun, 5 Feb 2017 21:59:36 +0100 Subject: Remove _XOPEN_SOURCE=500 -D__EXTENSIONS__ The value _XOPEN_SOURCE=600 should be used for C99 as we compile at least with C++11. By doing so the compilation reaches another error in a third library. Simply removing the option makes the compilation working normally. Task-number: QTBUG-56293 Change-Id: Ie040325936591958d05cc0a2d43643fa5d0c43b5 Reviewed-by: Oswald Buddenhagen --- mkspecs/solaris-g++-64/qmake.conf | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/mkspecs/solaris-g++-64/qmake.conf b/mkspecs/solaris-g++-64/qmake.conf index 6fc93d45b7..82cd77b888 100644 --- a/mkspecs/solaris-g++-64/qmake.conf +++ b/mkspecs/solaris-g++-64/qmake.conf @@ -7,23 +7,6 @@ # between GCC 2.95 or better and Solaris - but we still get warnings # because we don't use -isystem. # -# From the standards(5) manual page: -# The XNS4 specification is safe for use only in ILP32 (32-bit) -# environments and should not be used for LP64 (64-bit) -# application environments. Use XNS5, which has LP64-clean -# interfaces that are portable across ILP32 and LP64 environments. -# [...] -# For platforms supporting the LP64 (64-bit) programming environment -# where the SC5.0 Compilers have been installed, SUSv2-conforming LP64 -# applications using XNS5 library calls should be built with command -# lines of the form: -# c89 $(getconf XBS5_LP64_OFF64_CFLAGS) -D_XOPEN_SOURCE=500 \ -# $(getconf XBS5_LP64_OFF64_LDFLAGS) foo.c -o foo \ -# $(getconf XBS5_LP64_OFF64_LIBS) -lxnet -# So it appears that _XOPEN_SOURCE=500 should be defined when building -# 64-bit applications (on Solaris 7 and better). But then __EXTENSIONS__ -# should be defined as well to recover all the default system interface. -# MAKEFILE_GENERATOR = UNIX CONFIG += incremental @@ -31,7 +14,7 @@ QMAKE_INCREMENTAL_STYLE = sublib include(../common/solaris.conf) -QMAKE_CFLAGS = -m64 -D_XOPEN_SOURCE=500 -D__EXTENSIONS__ +QMAKE_CFLAGS = -m64 QMAKE_LFLAGS = -m64 QMAKE_LIBDIR_X11 = /usr/X11/lib/64 -- cgit v1.2.3 From 6c8aabbe5232314916084e4a1eaec6e9f03494a2 Mon Sep 17 00:00:00 2001 From: Julien Gueytat Date: Sun, 5 Feb 2017 22:03:37 +0100 Subject: Remove out of date comments related to Xorg and GCC Task-number: QTBUG-56293 Change-Id: I8d2245755d08b528e2041a16aabb390f3796e545 Reviewed-by: Oswald Buddenhagen --- mkspecs/solaris-g++-64/qmake.conf | 6 ------ mkspecs/solaris-g++/qmake.conf | 6 ------ 2 files changed, 12 deletions(-) diff --git a/mkspecs/solaris-g++-64/qmake.conf b/mkspecs/solaris-g++-64/qmake.conf index 82cd77b888..f509555ad9 100644 --- a/mkspecs/solaris-g++-64/qmake.conf +++ b/mkspecs/solaris-g++-64/qmake.conf @@ -1,12 +1,6 @@ # # qmake configuration for solaris-g++-64 # -# The X11 header files used to be broken on Solaris until patches were -# released in early 2001 for Solaris 2.6, 7, and 8. On Solaris 2.5.1 -# or non-patched systems -fpermissive works around the incompatibility -# between GCC 2.95 or better and Solaris - but we still get warnings -# because we don't use -isystem. -# MAKEFILE_GENERATOR = UNIX CONFIG += incremental diff --git a/mkspecs/solaris-g++/qmake.conf b/mkspecs/solaris-g++/qmake.conf index 943b0865c9..7c6c9d04a4 100644 --- a/mkspecs/solaris-g++/qmake.conf +++ b/mkspecs/solaris-g++/qmake.conf @@ -1,12 +1,6 @@ # # qmake configuration for solaris-g++ # -# The X11 header files used to be broken on Solaris until patches were -# released in early 2001 for Solaris 2.6, 7, and 8. On Solaris 2.5.1 -# or non-patched systems -fpermissive works around the incompatibility -# between GCC 2.95 or better and Solaris - but we still get warnings -# because we don't use -isystem. -# MAKEFILE_GENERATOR = UNIX CONFIG += incremental -- cgit v1.2.3 From 4f3ea309222bf9af61f721b39265b651ab297914 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Mon, 6 Feb 2017 19:41:46 -0800 Subject: Avoid unnecessary creation of some Q_GLOBAL_STATIC If these lists weren't created in the first place, then they are empty. We don't need to create it in order to conclude that. Unlike most Q_GLOBAL_STATICS, these are almost never used and yet they were always created due to where they were checked. Since we're calling exists() before, there are two consequences: first, since the list already exists, we're not allocating memory so it cannot throw std::bad_alloc when being accessed. Second, since we've just checked it exists, we can use QGlobalStatic's operator*(), which is slightly faster than operator()(). The weird &(*list) syntax is only to avoid changing the rest of the code that used a pointer Change-Id: Ifaee7464122d402991b6fffd14a0e44f533dc3d9 Reviewed-by: Marc Mutz --- src/corelib/global/qglobal.cpp | 5 ++++- src/corelib/kernel/qcoreapplication.cpp | 17 ++++++----------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/corelib/global/qglobal.cpp b/src/corelib/global/qglobal.cpp index bc6560ddae..54df8b1f61 100644 --- a/src/corelib/global/qglobal.cpp +++ b/src/corelib/global/qglobal.cpp @@ -4027,7 +4027,10 @@ bool QInternal::activateCallbacks(Callback cb, void **parameters) { Q_ASSERT_X(cb >= 0, "QInternal::activateCallback()", "Callback id must be a valid id"); - QInternal_CallBackTable *cbt = global_callback_table(); + if (!global_callback_table.exists()) + return false; + + QInternal_CallBackTable *cbt = &(*global_callback_table); if (cbt && cb < cbt->callbacks.size()) { QList callbacks = cbt->callbacks[cb]; bool ret = false; diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp index 96167b4508..c5f2e71f8c 100644 --- a/src/corelib/kernel/qcoreapplication.cpp +++ b/src/corelib/kernel/qcoreapplication.cpp @@ -270,12 +270,13 @@ void qRemovePostRoutine(QtCleanUpFunction p) static void qt_call_pre_routines() { - QStartUpFuncList *list = preRList(); - if (!list) + if (!preRList.exists()) return; + #ifndef QT_NO_THREAD QMutexLocker locker(&globalPreRoutinesMutex); #endif + QVFuncList *list = &(*preRList); // Unlike qt_call_post_routines, we don't empty the list, because // Q_COREAPP_STARTUP_FUNCTION is a macro, so the user expects // the function to be executed every time QCoreApplication is created. @@ -285,16 +286,10 @@ static void qt_call_pre_routines() void Q_CORE_EXPORT qt_call_post_routines() { - QVFuncList *list = 0; - QT_TRY { - list = postRList(); - } QT_CATCH(const std::bad_alloc &) { - // ignore - if we can't allocate a post routine list, - // there's a high probability that there's no post - // routine to be executed :) - } - if (!list) + if (!postRList.exists()) return; + + QVFuncList *list = &(*postRList); while (!list->isEmpty()) (list->takeFirst())(); } -- cgit v1.2.3 From 136c5b9338f71775eb42528cfc7c23b2b4e5dff9 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Mon, 6 Feb 2017 20:05:02 -0800 Subject: Merge several Q_GLOBAL_STATICs in qresource.cpp into one Since they are all used in a typical application, this reduces the number of memory allocations (thus, the overhead) as well as the state-keeping in the libc atexit() functions. Change-Id: Ifaee7464122d402991b6fffd14a0e59457ad9cb7 Reviewed-by: Olivier Goffart (Woboq GmbH) --- src/corelib/io/qresource.cpp | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/corelib/io/qresource.cpp b/src/corelib/io/qresource.cpp index febf22639c..32639759e4 100644 --- a/src/corelib/io/qresource.cpp +++ b/src/corelib/io/qresource.cpp @@ -149,12 +149,23 @@ static QString cleanPath(const QString &_path) Q_DECLARE_TYPEINFO(QResourceRoot, Q_MOVABLE_TYPE); -Q_GLOBAL_STATIC_WITH_ARGS(QMutex, resourceMutex, (QMutex::Recursive)) - typedef QList ResourceList; -Q_GLOBAL_STATIC(ResourceList, resourceList) +struct QResourceGlobalData +{ + QMutex resourceMutex{QMutex::Recursive}; + ResourceList resourceList; + QStringList resourceSearchPaths; +}; +Q_GLOBAL_STATIC(QResourceGlobalData, resourceGlobalData) + +static inline QMutex *resourceMutex() +{ return &resourceGlobalData->resourceMutex; } -Q_GLOBAL_STATIC(QStringList, resourceSearchPaths) +static inline ResourceList *resourceList() +{ return &resourceGlobalData->resourceList; } + +static inline QStringList *resourceSearchPaths() +{ return &resourceGlobalData->resourceSearchPaths; } /*! \class QResource @@ -870,6 +881,9 @@ Q_CORE_EXPORT bool qRegisterResourceData(int version, const unsigned char *tree, Q_CORE_EXPORT bool qUnregisterResourceData(int version, const unsigned char *tree, const unsigned char *name, const unsigned char *data) { + if (resourceGlobalData.isDestroyed()) + return false; + QMutexLocker lock(resourceMutex()); if ((version == 0x01 || version == 0x02) && resourceList()) { QResourceRoot res(version, tree, name, data); -- cgit v1.2.3 From 0f2638c994f665bfca2d40cd5b40cc25dac8023e Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Wed, 8 Feb 2017 12:04:54 +0100 Subject: Blacklist tst_QElapsedTimer::elapsed() on Windows This test was determined to be flaky on the CI. Task-number: QTBUG-58713 Change-Id: Ie6e6a69b8ea625e3a3102c88d52f1f0fbec242aa Reviewed-by: Lars Knoll --- tests/auto/corelib/kernel/qelapsedtimer/BLACKLIST | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 tests/auto/corelib/kernel/qelapsedtimer/BLACKLIST diff --git a/tests/auto/corelib/kernel/qelapsedtimer/BLACKLIST b/tests/auto/corelib/kernel/qelapsedtimer/BLACKLIST new file mode 100644 index 0000000000..f6a49f032c --- /dev/null +++ b/tests/auto/corelib/kernel/qelapsedtimer/BLACKLIST @@ -0,0 +1,2 @@ +[elapsed] +windows -- cgit v1.2.3 From 559bfa09e4000d32bbfeba2d6f27e5f722450734 Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Thu, 9 Feb 2017 15:20:04 +0100 Subject: refuse to build EGLFS on android, darwin, and windows ... instead of merely defaulting it to off on android and windows. this reflects actual reality. Change-Id: I880254138bedd07124aa00096a06dd6e1803feb9 Reviewed-by: Laszlo Agocs --- src/gui/configure.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/gui/configure.json b/src/gui/configure.json index 35741abc86..2dc70c5ea2 100644 --- a/src/gui/configure.json +++ b/src/gui/configure.json @@ -600,8 +600,7 @@ "eglfs": { "label": "EGLFS", "section": "Platform plugins", - "autoDetect": "!config.android && !config.win32", - "condition": "features.egl", + "condition": "!config.android && !config.darwin && !config.win32 && features.egl", "output": [ "privateFeature" ] }, "eglfs_brcm": { -- cgit v1.2.3 From 2a48f7b189ab578b13bd2b9b459c2d096ba519dd Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Thu, 9 Feb 2017 15:18:38 +0100 Subject: make libinput axis api feature depend on libinput amends b4085e56. Change-Id: Id18a7de6496e8e9164cb247426aba1293aa4ea2e Reviewed-by: Laszlo Agocs --- src/gui/configure.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/configure.json b/src/gui/configure.json index 2dc70c5ea2..9b93bcf3e2 100644 --- a/src/gui/configure.json +++ b/src/gui/configure.json @@ -511,7 +511,7 @@ }, "libinput-axis-api": { "label": "axis API in libinput", - "condition": "tests.libinput_axis_api", + "condition": "features.libinput && tests.libinput_axis_api", "output": [ "privateFeature" ] }, "lgmon": { -- cgit v1.2.3 From b052d0cffd30ba488bf73c8ee57085e5c023298f Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Wed, 8 Feb 2017 13:19:44 +0100 Subject: rename qvector.cpp => qvector.qdoc the file contains no code. this avoids complaints from ar/ranlib in static/bootstrapped builds. Change-Id: Iee22ffc61a5f9ea8c25f5455b7e8e017ac521624 Reviewed-by: Marc Mutz Reviewed-by: Thiago Macieira --- qmake/Makefile.unix | 7 +- qmake/Makefile.win32 | 1 - qmake/qmake.pri | 1 - src/corelib/tools/qvector.cpp | 1346 ------------------------------------- src/corelib/tools/qvector.qdoc | 1346 +++++++++++++++++++++++++++++++++++++ src/corelib/tools/tools.pri | 1 - src/tools/bootstrap/bootstrap.pro | 1 - 7 files changed, 1348 insertions(+), 1355 deletions(-) delete mode 100644 src/corelib/tools/qvector.cpp create mode 100644 src/corelib/tools/qvector.qdoc diff --git a/qmake/Makefile.unix b/qmake/Makefile.unix index 8eb54f554e..eec4ef4984 100644 --- a/qmake/Makefile.unix +++ b/qmake/Makefile.unix @@ -14,7 +14,7 @@ OBJS=project.o option.o property.o main.o ioutils.o proitems.o \ QOBJS=qtextcodec.o qutfcodec.o qstring.o qstring_compat.o qstringbuilder.o qtextstream.o qiodevice.o \ qringbuffer.o qdebug.o qmalloc.o qglobal.o \ qarraydata.o qbytearray.o qbytearraymatcher.o qdatastream.o qbuffer.o qlist.o qfiledevice.o qfile.o \ - qfilesystementry.o qfilesystemengine.o qfsfileengine.o qfsfileengine_iterator.o qregexp.o qvector.o \ + qfilesystementry.o qfilesystemengine.o qfsfileengine.o qfsfileengine_iterator.o qregexp.o \ qbitarray.o qdir.o qdiriterator.o quuid.o qhash.o qfileinfo.o qdatetime.o qstringlist.o \ qabstractfileengine.o qtemporaryfile.o qmap.o qmetatype.o qsettings.o qsystemerror.o \ qvariant.o qvsnprintf.o qlocale.o qlocale_tools.o qlinkedlist.o qnumeric.o \ @@ -62,7 +62,7 @@ DEPEND_SRC = \ $(SOURCE_PATH)/src/corelib/io/qabstractfileengine.cpp \ $(SOURCE_PATH)/src/corelib/io/qfsfileengine_iterator.cpp \ $(SOURCE_PATH)/src/corelib/io/qfsfileengine.cpp $(SOURCE_PATH)/src/corelib/tools/qlist.cpp \ - $(SOURCE_PATH)/src/corelib/tools/qvector.cpp $(SOURCE_PATH)/src/corelib/tools/qbitarray.cpp \ + $(SOURCE_PATH)/src/corelib/tools/qbitarray.cpp \ $(SOURCE_PATH)/src/corelib/io/qdiriterator.cpp \ $(SOURCE_PATH)/src/corelib/io/qdir.cpp $(SOURCE_PATH)/src/corelib/plugin/quuid.cpp \ $(SOURCE_PATH)/src/corelib/io/qfileinfo.cpp $(SOURCE_PATH)/src/corelib/tools/qdatetime.cpp \ @@ -366,9 +366,6 @@ qtemporaryfile.o: $(SOURCE_PATH)/src/corelib/io/qtemporaryfile.cpp qregexp.o: $(SOURCE_PATH)/src/corelib/tools/qregexp.cpp $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qregexp.cpp -qvector.o: $(SOURCE_PATH)/src/corelib/tools/qvector.cpp - $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qvector.cpp - qbitarray.o: $(SOURCE_PATH)/src/corelib/tools/qbitarray.cpp $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qbitarray.cpp diff --git a/qmake/Makefile.win32 b/qmake/Makefile.win32 index bb1f8aaabc..6d2bcdbb78 100644 --- a/qmake/Makefile.win32 +++ b/qmake/Makefile.win32 @@ -106,7 +106,6 @@ QTOBJS= \ qtextstream.obj \ qdatastream.obj \ quuid.obj \ - qvector.obj \ qsettings.obj \ qvariant.obj \ qsettings_win.obj \ diff --git a/qmake/qmake.pri b/qmake/qmake.pri index 288d99d8a2..c0f7250bcf 100644 --- a/qmake/qmake.pri +++ b/qmake/qmake.pri @@ -68,7 +68,6 @@ bootstrap { #Qt code qlibraryinfo.cpp \ qsystemerror.cpp \ qvariant.cpp \ - qvector.cpp \ qvsnprintf.cpp \ qxmlstream.cpp \ qxmlutils.cpp \ diff --git a/src/corelib/tools/qvector.cpp b/src/corelib/tools/qvector.cpp deleted file mode 100644 index 0ea47b1a1a..0000000000 --- a/src/corelib/tools/qvector.cpp +++ /dev/null @@ -1,1346 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/*! - \class QVector - \inmodule QtCore - \brief The QVector class is a template class that provides a dynamic array. - - \ingroup tools - \ingroup shared - - \reentrant - - QVector\ is one of Qt's generic \l{container classes}. It - stores its items in adjacent memory locations and provides fast - index-based access. - - QList\, QLinkedList\, QVector\, and QVarLengthArray\ - provide similar APIs and functionality. They are often interchangeable, - but there are performance consequences. Here is an overview of use cases: - - \list - \li QVector should be your default first choice. - QVector\ will usually give better performance than QList\, - because QVector\ always stores its items sequentially in memory, - where QList\ will allocate its items on the heap unless - \c {sizeof(T) <= sizeof(void*)} and T has been declared to be - either a \c{Q_MOVABLE_TYPE} or a \c{Q_PRIMITIVE_TYPE} using - \l {Q_DECLARE_TYPEINFO}. See the \l {Pros and Cons of Using QList} - for an explanation. - \li However, QList is used throughout the Qt APIs for passing - parameters and for returning values. Use QList to interface with - those APIs. - \li If you need a real linked list, which guarantees - \l{Algorithmic Complexity}{constant time} insertions mid-list and - uses iterators to items rather than indexes, use QLinkedList. - \endlist - - \note QVector and QVarLengthArray both guarantee C-compatible - array layout. QList does not. This might be important if your - application must interface with a C API. - - \note Iterators into a QLinkedList and references into - heap-allocating QLists remain valid as long as the referenced items - remain in the container. This is not true for iterators and - references into a QVector and non-heap-allocating QLists. - - Here's an example of a QVector that stores integers and a QVector - that stores QString values: - - \snippet code/src_corelib_tools_qvector.cpp 0 - - QVector stores its items in a vector (array). Typically, vectors - are created with an initial size. For example, the following code - constructs a QVector with 200 elements: - - \snippet code/src_corelib_tools_qvector.cpp 1 - - The elements are automatically initialized with a - \l{default-constructed value}. If you want to initialize the - vector with a different value, pass that value as the second - argument to the constructor: - - \snippet code/src_corelib_tools_qvector.cpp 2 - - You can also call fill() at any time to fill the vector with a - value. - - QVector uses 0-based indexes, just like C++ arrays. To access the - item at a particular index position, you can use operator[](). On - non-const vectors, operator[]() returns a reference to the item - that can be used on the left side of an assignment: - - \snippet code/src_corelib_tools_qvector.cpp 3 - - For read-only access, an alternative syntax is to use at(): - - \snippet code/src_corelib_tools_qvector.cpp 4 - - at() can be faster than operator[](), because it never causes a - \l{deep copy} to occur. - - Another way to access the data stored in a QVector is to call - data(). The function returns a pointer to the first item in the - vector. You can use the pointer to directly access and modify the - elements stored in the vector. The pointer is also useful if you - need to pass a QVector to a function that accepts a plain C++ - array. - - If you want to find all occurrences of a particular value in a - vector, use indexOf() or lastIndexOf(). The former searches - forward starting from a given index position, the latter searches - backward. Both return the index of the matching item if they found - one; otherwise, they return -1. For example: - - \snippet code/src_corelib_tools_qvector.cpp 5 - - If you simply want to check whether a vector contains a - particular value, use contains(). If you want to find out how - many times a particular value occurs in the vector, use count(). - - QVector provides these basic functions to add, move, and remove - items: insert(), replace(), remove(), prepend(), append(). With - the exception of append() and replace(), these functions can be slow - (\l{linear time}) for large vectors, because they require moving many - items in the vector by one position in memory. If you want a container - class that provides fast insertion/removal in the middle, use - QList or QLinkedList instead. - - Unlike plain C++ arrays, QVectors can be resized at any time by - calling resize(). If the new size is larger than the old size, - QVector might need to reallocate the whole vector. QVector tries - to reduce the number of reallocations by preallocating up to twice - as much memory as the actual data needs. - - If you know in advance approximately how many items the QVector - will contain, you can call reserve(), asking QVector to - preallocate a certain amount of memory. You can also call - capacity() to find out how much memory QVector actually - allocated. - - Note that using non-const operators and functions can cause - QVector to do a deep copy of the data. This is due to \l{implicit sharing}. - - QVector's value type must be an \l{assignable data type}. This - covers most data types that are commonly used, but the compiler - won't let you, for example, store a QWidget as a value; instead, - store a QWidget *. A few functions have additional requirements; - for example, indexOf() and lastIndexOf() expect the value type to - support \c operator==(). These requirements are documented on a - per-function basis. - - Like the other container classes, QVector provides \l{Java-style - iterators} (QVectorIterator and QMutableVectorIterator) and - \l{STL-style iterators} (QVector::const_iterator and - QVector::iterator). In practice, these are rarely used, because - you can use indexes into the QVector. - - In addition to QVector, Qt also provides QVarLengthArray, a very - low-level class with little functionality that is optimized for - speed. - - QVector does \e not support inserting, prepending, appending or replacing - with references to its own values. Doing so will cause your application to - abort with an error message. - - \section2 More Information on Using Qt Containers - - For a detailed discussion comparing Qt containers with each other and - with STL containers, see \l {Understand the Qt Containers}. - - \sa QVectorIterator, QMutableVectorIterator, QList, QLinkedList -*/ - -/*! - \fn QVector QVector::mid(int pos, int length = -1) const - - Returns a sub-vector which contains elements from this vector, - starting at position \a pos. If \a length is -1 (the default), all - elements after \a pos are included; otherwise \a length elements (or - all remaining elements if there are less than \a length elements) - are included. -*/ - - -/*! \fn QVector::QVector() - - Constructs an empty vector. - - \sa resize() -*/ - -/*! - \fn QVector::QVector(QVector &&other) - - Move-constructs a QVector instance, making it point at the same - object that \a other was pointing to. - - \since 5.2 -*/ - -/*! \fn QVector::QVector(int size) - - Constructs a vector with an initial size of \a size elements. - - The elements are initialized with a \l{default-constructed - value}. - - \sa resize() -*/ - -/*! \fn QVector::QVector(int size, const T &value) - - Constructs a vector with an initial size of \a size elements. - Each element is initialized with \a value. - - \sa resize(), fill() -*/ - -/*! \fn QVector::QVector(const QVector &other) - - Constructs a copy of \a other. - - This operation takes \l{Algorithmic Complexity}{constant time}, - because QVector is \l{implicitly shared}. This makes returning - a QVector from a function very fast. If a shared instance is - modified, it will be copied (copy-on-write), and that takes - \l{Algorithmic Complexity}{linear time}. - - \sa operator=() -*/ - -/*! \fn QVector::QVector(std::initializer_list args) - \since 4.8 - - Constructs a vector from the std::initializer_list given by \a args. - - This constructor is only enabled if the compiler supports C++11 initializer - lists. -*/ - - -/*! \fn QVector::~QVector() - - Destroys the vector. -*/ - -/*! \fn QVector &QVector::operator=(const QVector &other) - - Assigns \a other to this vector and returns a reference to this - vector. -*/ - -/*! - \fn QVector &QVector::operator=(QVector &&other) - - Move-assigns \a other to this QVector instance. - - \since 5.2 -*/ - -/*! \fn void QVector::swap(QVector &other) - \since 4.8 - - Swaps vector \a other with this vector. This operation is very fast and - never fails. -*/ - -/*! \fn bool QVector::operator==(const QVector &other) const - - Returns \c true if \a other is equal to this vector; otherwise - returns \c false. - - Two vectors are considered equal if they contain the same values - in the same order. - - This function requires the value type to have an implementation - of \c operator==(). - - \sa operator!=() -*/ - -/*! \fn bool QVector::operator!=(const QVector &other) const - - Returns \c true if \a other is not equal to this vector; otherwise - returns \c false. - - Two vectors are considered equal if they contain the same values - in the same order. - - This function requires the value type to have an implementation - of \c operator==(). - - \sa operator==() -*/ - -/*! \fn bool operator<(const QVector &lhs, const QVector &rhs) - \since 5.6 - \relates QVector - - Returns \c true if vector \a lhs is - \l{http://en.cppreference.com/w/cpp/algorithm/lexicographical_compare} - {lexicographically less than} \a rhs; otherwise returns \c false. - - This function requires the value type to have an implementation - of \c operator<(). -*/ - -/*! \fn bool operator<=(const QVector &lhs, const QVector &rhs) - \since 5.6 - \relates QVector - - Returns \c true if vector \a lhs is - \l{http://en.cppreference.com/w/cpp/algorithm/lexicographical_compare} - {lexicographically less than or equal to} \a rhs; otherwise returns \c false. - - This function requires the value type to have an implementation - of \c operator<(). -*/ - -/*! \fn bool operator>(const QVector &lhs, const QVector &rhs) - \since 5.6 - \relates QVector - - Returns \c true if vector \a lhs is - \l{http://en.cppreference.com/w/cpp/algorithm/lexicographical_compare} - {lexicographically greater than} \a rhs; otherwise returns \c false. - - This function requires the value type to have an implementation - of \c operator<(). -*/ - -/*! \fn bool operator>=(const QVector &lhs, const QVector &rhs) - \since 5.6 - \relates QVector - - Returns \c true if vector \a lhs is - \l{http://en.cppreference.com/w/cpp/algorithm/lexicographical_compare} - {lexicographically greater than or equal to} \a rhs; otherwise returns \c false. - - This function requires the value type to have an implementation - of \c operator<(). -*/ - -/*! - \fn uint qHash(const QVector &key, uint seed = 0) - \since 5.6 - \relates QVector - - Returns the hash value for \a key, - using \a seed to seed the calculation. - - This function requires qHash() to be overloaded for the value type \c T. -*/ - -/*! \fn int QVector::size() const - - Returns the number of items in the vector. - - \sa isEmpty(), resize() -*/ - -/*! \fn bool QVector::isEmpty() const - - Returns \c true if the vector has size 0; otherwise returns \c false. - - \sa size(), resize() -*/ - -/*! \fn void QVector::resize(int size) - - Sets the size of the vector to \a size. If \a size is greater than the - current size, elements are added to the end; the new elements are - initialized with a \l{default-constructed value}. If \a size is less - than the current size, elements are removed from the end. - - Since Qt 5.6, resize() doesn't shrink the capacity anymore. - To shed excess capacity, use squeeze(). - - \sa size() -*/ - -/*! \fn int QVector::capacity() const - - Returns the maximum number of items that can be stored in the - vector without forcing a reallocation. - - The sole purpose of this function is to provide a means of fine - tuning QVector's memory usage. In general, you will rarely ever - need to call this function. If you want to know how many items are - in the vector, call size(). - - \sa reserve(), squeeze() -*/ - -/*! \fn void QVector::reserve(int size) - - Attempts to allocate memory for at least \a size elements. If you - know in advance how large the vector will be, you should call this - function to prevent reallocations and memory fragmentation. - - If \a size is an underestimate, the worst that will happen is that - the QVector will be a bit slower. If \a size is an overestimate, you - may have used more memory than the normal QVector growth strategy - would have allocated—or you may have used less. - - An alternative to reserve() is calling resize(). Whether or not that is - faster than reserve() depends on the element type, because resize() - default-constructs all elements, and requires assignment to existing - entries rather than calling append(), which copy- or move-constructs. - For simple types, like \c int or \c double, resize() is typically faster, - but for anything more complex, you should prefer reserve(). - - \warning If the size passed to resize() was underestimated, you run out - of allocated space and into undefined behavior. This problem does not - exist with reserve(), because it treats the size as just a hint. - - \sa squeeze(), capacity() -*/ - -/*! \fn void QVector::squeeze() - - Releases any memory not required to store the items. - - The sole purpose of this function is to provide a means of fine - tuning QVector's memory usage. In general, you will rarely ever - need to call this function. - - \sa reserve(), capacity() -*/ - -/*! \fn void QVector::detach() - - \internal -*/ - -/*! \fn bool QVector::isDetached() const - - \internal -*/ - -/*! \fn void QVector::setSharable(bool sharable) - - \internal -*/ - -/*! \fn bool QVector::isSharedWith(const QVector &other) const - - \internal -*/ - -/*! \fn T *QVector::data() - - Returns a pointer to the data stored in the vector. The pointer - can be used to access and modify the items in the vector. - - Example: - \snippet code/src_corelib_tools_qvector.cpp 6 - - The pointer remains valid as long as the vector isn't - reallocated. - - This function is mostly useful to pass a vector to a function - that accepts a plain C++ array. - - \sa constData(), operator[]() -*/ - -/*! \fn const T *QVector::data() const - - \overload -*/ - -/*! \fn const T *QVector::constData() const - - Returns a const pointer to the data stored in the vector. The - pointer can be used to access the items in the vector. - The pointer remains valid as long as the vector isn't - reallocated. - - This function is mostly useful to pass a vector to a function - that accepts a plain C++ array. - - \sa data(), operator[]() -*/ - -/*! \fn void QVector::clear() - - Removes all the elements from the vector. - - \note Until Qt 5.6, this also released the memory used by - the vector. From Qt 5.7, the capacity is preserved. To shed - all capacity, swap with a default-constructed vector: - \code - QVector v ...; - QVector().swap(v); - Q_ASSERT(v.capacity() == 0); - \endcode - or call squeeze(). - - \sa squeeze() -*/ - -/*! \fn const T &QVector::at(int i) const - - Returns the item at index position \a i in the vector. - - \a i must be a valid index position in the vector (i.e., 0 <= \a - i < size()). - - \sa value(), operator[]() -*/ - -/*! \fn T &QVector::operator[](int i) - - Returns the item at index position \a i as a modifiable reference. - - \a i must be a valid index position in the vector (i.e., 0 <= \a i - < size()). - - Note that using non-const operators can cause QVector to do a deep - copy. - - \sa at(), value() -*/ - -/*! \fn const T &QVector::operator[](int i) const - - \overload - - Same as at(\a i). -*/ - -/*! - \fn void QVector::append(const T &value) - - Inserts \a value at the end of the vector. - - Example: - \snippet code/src_corelib_tools_qvector.cpp 7 - - This is the same as calling resize(size() + 1) and assigning \a - value to the new last element in the vector. - - This operation is relatively fast, because QVector typically - allocates more memory than necessary, so it can grow without - reallocating the entire vector each time. - - \sa operator<<(), prepend(), insert() -*/ - -/*! - \fn void QVector::append(T &&value) - \since 5.6 - - \overload - - Example: - \snippet code/src_corelib_tools_qvector.cpp move-append -*/ - -/*! \fn void QVector::append(const QVector &value) - - \overload - - \since 5.5 - - Appends the items of the \a value vector to this vector. - - \sa operator<<(), operator+=() -*/ - - -/*! \fn void QVector::prepend(const T &value) - - Inserts \a value at the beginning of the vector. - - Example: - \snippet code/src_corelib_tools_qvector.cpp 8 - - This is the same as vector.insert(0, \a value). - - For large vectors, this operation can be slow (\l{linear time}), - because it requires moving all the items in the vector by one - position further in memory. If you want a container class that - provides a fast prepend() function, use QList or QLinkedList - instead. - - \sa append(), insert() -*/ - -/*! \fn void QVector::insert(int i, const T &value) - - Inserts \a value at index position \a i in the vector. If \a i is - 0, the value is prepended to the vector. If \a i is size(), the - value is appended to the vector. - - Example: - \snippet code/src_corelib_tools_qvector.cpp 9 - - For large vectors, this operation can be slow (\l{linear time}), - because it requires moving all the items at indexes \a i and - above by one position further in memory. If you want a container - class that provides a fast insert() function, use QLinkedList - instead. - - \sa append(), prepend(), remove() -*/ - -/*! \fn void QVector::insert(int i, int count, const T &value) - - \overload - - Inserts \a count copies of \a value at index position \a i in the - vector. - - Example: - \snippet code/src_corelib_tools_qvector.cpp 10 -*/ - -/*! \fn QVector::iterator QVector::insert(iterator before, const T &value) - - \overload - - Inserts \a value in front of the item pointed to by the iterator - \a before. Returns an iterator pointing at the inserted item. -*/ - -/*! \fn QVector::iterator QVector::insert(iterator before, int count, const T &value) - - Inserts \a count copies of \a value in front of the item pointed to - by the iterator \a before. Returns an iterator pointing at the - first of the inserted items. -*/ - -/*! \fn void QVector::replace(int i, const T &value) - - Replaces the item at index position \a i with \a value. - - \a i must be a valid index position in the vector (i.e., 0 <= \a - i < size()). - - \sa operator[](), remove() -*/ - -/*! \fn void QVector::remove(int i) - - \overload - - Removes the element at index position \a i. - - \sa insert(), replace(), fill() -*/ - -/*! \fn void QVector::remove(int i, int count) - - \overload - - Removes \a count elements from the middle of the vector, starting at - index position \a i. - - \sa insert(), replace(), fill() -*/ - -/*! \fn void QVector::removeAt(int i) - \since 5.2 - - Removes the element at index position \a i. - Equivalent to - \code - remove(i); - \endcode - - Provided for compatibility with QList. - - \sa remove(), QList::removeAt() -*/ - -/*! \fn int QVector::removeAll(const T &t) - \since 5.4 - - Removes all elements that compare equal to \a t from the - vector. Returns the number of elements removed, if any. - - Provided for compatibility with QList. - - \sa removeOne(), QList::removeAll() -*/ - -/*! \fn bool QVector::removeOne(const T &t) - \since 5.4 - - Removes the first element that compares equal to \a t from the - vector. Returns whether an element was, in fact, removed. - - Provided for compatibility with QList. - - \sa removeAll(), QList::removeOne() -*/ - -/*! \fn int QVector::length() const - \since 5.2 - - Same as size() and count(). - - Provided for compatibility with QList. - - \sa size(), count(), QList::length() -*/ - -/*! \fn T QVector::takeAt(int i) - \since 5.2 - - Removes the element at index position \a i and returns it. - - Equivalent to - \code - T t = at(i); - remove(i); - return t; - \endcode - - Provided for compatibility with QList. - - \sa takeFirst(), takeLast(), QList::takeAt() -*/ - -/*! \fn void QVector::move(int from, int to) - \since 5.6 - - Moves the item at index position \a from to index position \a to. - - Provided for compatibility with QList. - - \sa QList::move() -*/ - -/*! \fn void QVector::removeFirst() - \since 5.1 - Removes the first item in the vector. Calling this function is - equivalent to calling remove(0). The vector must not be empty. If - the vector can be empty, call isEmpty() before calling this - function. - - \sa remove(), takeFirst(), isEmpty() -*/ - -/*! \fn void QVector::removeLast() - \since 5.1 - Removes the last item in the vector. Calling this function is - equivalent to calling remove(size() - 1). The vector must not be - empty. If the vector can be empty, call isEmpty() before calling - this function. - - \sa remove(), takeLast(), removeFirst(), isEmpty() -*/ - -/*! \fn T QVector::takeFirst() - \since 5.1 - - Removes the first item in the vector and returns it. This function - assumes the vector is not empty. To avoid failure, call isEmpty() - before calling this function. - - \sa takeLast(), removeFirst() -*/ - -/*! \fn T QVector::takeLast() - \since 5.1 - - Removes the last item in the list and returns it. This function - assumes the vector is not empty. To avoid failure, call isEmpty() - before calling this function. - - If you don't use the return value, removeLast() is more - efficient. - - \sa takeFirst(), removeLast() -*/ - - -/*! \fn QVector &QVector::fill(const T &value, int size = -1) - - Assigns \a value to all items in the vector. If \a size is - different from -1 (the default), the vector is resized to size \a - size beforehand. - - Example: - \snippet code/src_corelib_tools_qvector.cpp 11 - - \sa resize() -*/ - -/*! \fn int QVector::indexOf(const T &value, int from = 0) const - - Returns the index position of the first occurrence of \a value in - the vector, searching forward from index position \a from. - Returns -1 if no item matched. - - Example: - \snippet code/src_corelib_tools_qvector.cpp 12 - - This function requires the value type to have an implementation of - \c operator==(). - - \sa lastIndexOf(), contains() -*/ - -/*! \fn int QVector::lastIndexOf(const T &value, int from = -1) const - - Returns the index position of the last occurrence of the value \a - value in the vector, searching backward from index position \a - from. If \a from is -1 (the default), the search starts at the - last item. Returns -1 if no item matched. - - Example: - \snippet code/src_corelib_tools_qvector.cpp 13 - - This function requires the value type to have an implementation of - \c operator==(). - - \sa indexOf() -*/ - -/*! \fn bool QVector::contains(const T &value) const - - Returns \c true if the vector contains an occurrence of \a value; - otherwise returns \c false. - - This function requires the value type to have an implementation of - \c operator==(). - - \sa indexOf(), count() -*/ - -/*! \fn bool QVector::startsWith(const T &value) const - \since 4.5 - - Returns \c true if this vector is not empty and its first - item is equal to \a value; otherwise returns \c false. - - \sa isEmpty(), first() -*/ - -/*! \fn bool QVector::endsWith(const T &value) const - \since 4.5 - - Returns \c true if this vector is not empty and its last - item is equal to \a value; otherwise returns \c false. - - \sa isEmpty(), last() -*/ - - -/*! \fn int QVector::count(const T &value) const - - Returns the number of occurrences of \a value in the vector. - - This function requires the value type to have an implementation of - \c operator==(). - - \sa contains(), indexOf() -*/ - -/*! \fn int QVector::count() const - - \overload - - Same as size(). -*/ - -/*! \fn QVector::iterator QVector::begin() - - Returns an \l{STL-style iterators}{STL-style iterator} pointing to the first item in - the vector. - - \sa constBegin(), end() -*/ - -/*! \fn QVector::const_iterator QVector::begin() const - - \overload -*/ - -/*! \fn QVector::const_iterator QVector::cbegin() const - \since 5.0 - - Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first item - in the vector. - - \sa begin(), cend() -*/ - -/*! \fn QVector::const_iterator QVector::constBegin() const - - Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first item - in the vector. - - \sa begin(), constEnd() -*/ - -/*! \fn QVector::iterator QVector::end() - - Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item - after the last item in the vector. - - \sa begin(), constEnd() -*/ - -/*! \fn QVector::const_iterator QVector::end() const - - \overload -*/ - -/*! \fn QVector::const_iterator QVector::cend() const - \since 5.0 - - Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary - item after the last item in the vector. - - \sa cbegin(), end() -*/ - -/*! \fn QVector::const_iterator QVector::constEnd() const - - Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary - item after the last item in the vector. - - \sa constBegin(), end() -*/ - -/*! \fn QVector::reverse_iterator QVector::rbegin() - \since 5.6 - - Returns a \l{STL-style iterators}{STL-style} reverse iterator pointing to the first - item in the vector, in reverse order. - - \sa begin(), crbegin(), rend() -*/ - -/*! \fn QVector::const_reverse_iterator QVector::rbegin() const - \since 5.6 - \overload -*/ - -/*! \fn QVector::const_reverse_iterator QVector::crbegin() const - \since 5.6 - - Returns a const \l{STL-style iterators}{STL-style} reverse iterator pointing to the first - item in the vector, in reverse order. - - \sa begin(), rbegin(), rend() -*/ - -/*! \fn QVector::reverse_iterator QVector::rend() - \since 5.6 - - Returns a \l{STL-style iterators}{STL-style} reverse iterator pointing to one past - the last item in the vector, in reverse order. - - \sa end(), crend(), rbegin() -*/ - -/*! \fn QVector::const_reverse_iterator QVector::rend() const - \since 5.6 - \overload -*/ - -/*! \fn QVector::const_reverse_iterator QVector::crend() const - \since 5.6 - - Returns a const \l{STL-style iterators}{STL-style} reverse iterator pointing to one - past the last item in the vector, in reverse order. - - \sa end(), rend(), rbegin() -*/ - -/*! \fn QVector::iterator QVector::erase(iterator pos) - - Removes the item pointed to by the iterator \a pos from the - vector, and returns an iterator to the next item in the vector - (which may be end()). - - \sa insert(), remove() -*/ - -/*! \fn QVector::iterator QVector::erase(iterator begin, iterator end) - - \overload - - Removes all the items from \a begin up to (but not including) \a - end. Returns an iterator to the same item that \a end referred to - before the call. -*/ - -/*! \fn T& QVector::first() - - Returns a reference to the first item in the vector. This - function assumes that the vector isn't empty. - - \sa last(), isEmpty(), constFirst() -*/ - -/*! \fn const T& QVector::first() const - - \overload -*/ - -/*! \fn const T& QVector::constFirst() const - \since 5.6 - - Returns a const reference to the first item in the vector. This - function assumes that the vector isn't empty. - - \sa constLast(), isEmpty(), first() -*/ - -/*! \fn T& QVector::last() - - Returns a reference to the last item in the vector. This function - assumes that the vector isn't empty. - - \sa first(), isEmpty(), constLast() -*/ - -/*! \fn const T& QVector::last() const - - \overload -*/ - -/*! \fn const T& QVector::constLast() const - \since 5.6 - - Returns a const reference to the last item in the vector. This function - assumes that the vector isn't empty. - - \sa constFirst(), isEmpty(), last() -*/ - -/*! \fn T QVector::value(int i) const - - Returns the value at index position \a i in the vector. - - If the index \a i is out of bounds, the function returns - a \l{default-constructed value}. If you are certain that - \a i is within bounds, you can use at() instead, which is slightly - faster. - - \sa at(), operator[]() -*/ - -/*! \fn T QVector::value(int i, const T &defaultValue) const - - \overload - - If the index \a i is out of bounds, the function returns - \a defaultValue. -*/ - -/*! \fn void QVector::push_back(const T &value) - - This function is provided for STL compatibility. It is equivalent - to append(\a value). -*/ - -/*! \fn void QVector::push_back(T &&value) - \since 5.6 - \overload -*/ - -/*! \fn void QVector::push_front(const T &value) - - This function is provided for STL compatibility. It is equivalent - to prepend(\a value). -*/ - -/*! \fn void QVector::pop_front() - - This function is provided for STL compatibility. It is equivalent - to removeFirst(). -*/ - -/*! \fn void QVector::pop_back() - - This function is provided for STL compatibility. It is equivalent - to removeLast(). -*/ - -/*! \fn T& QVector::front() - - This function is provided for STL compatibility. It is equivalent - to first(). -*/ - -/*! \fn QVector::const_reference QVector::front() const - - \overload -*/ - -/*! \fn QVector::reference QVector::back() - - This function is provided for STL compatibility. It is equivalent - to last(). -*/ - -/*! \fn QVector::const_reference QVector::back() const - - \overload -*/ - -/*! \fn bool QVector::empty() const - - This function is provided for STL compatibility. It is equivalent - to isEmpty(), returning \c true if the vector is empty; otherwise - returns \c false. -*/ - -/*! \fn QVector &QVector::operator+=(const QVector &other) - - Appends the items of the \a other vector to this vector and - returns a reference to this vector. - - \sa operator+(), append() -*/ - -/*! \fn void QVector::operator+=(const T &value) - - \overload - - Appends \a value to the vector. - - \sa append(), operator<<() -*/ - -/*! \fn QVector QVector::operator+(const QVector &other) const - - Returns a vector that contains all the items in this vector - followed by all the items in the \a other vector. - - \sa operator+=() -*/ - -/*! \fn QVector &QVector::operator<<(const T &value) - - Appends \a value to the vector and returns a reference to this - vector. - - \sa append(), operator+=() -*/ - -/*! \fn QVector &QVector::operator<<(const QVector &other) - - Appends \a other to the vector and returns a reference to the - vector. -*/ - -/*! \typedef QVector::iterator - - The QVector::iterator typedef provides an STL-style non-const - iterator for QVector and QStack. - - QVector provides both \l{STL-style iterators} and \l{Java-style - iterators}. The STL-style non-const iterator is simply a typedef - for "T *" (pointer to T). - - \warning Iterators on implicitly shared containers do not work - exactly like STL-iterators. You should avoid copying a container - while iterators are active on that container. For more information, - read \l{Implicit sharing iterator problem}. - - \sa QVector::begin(), QVector::end(), QVector::const_iterator, QMutableVectorIterator -*/ - -/*! \typedef QVector::const_iterator - - The QVector::const_iterator typedef provides an STL-style const - iterator for QVector and QStack. - - QVector provides both \l{STL-style iterators} and \l{Java-style - iterators}. The STL-style const iterator is simply a typedef for - "const T *" (pointer to const T). - - \warning Iterators on implicitly shared containers do not work - exactly like STL-iterators. You should avoid copying a container - while iterators are active on that container. For more information, - read \l{Implicit sharing iterator problem}. - - \sa QVector::constBegin(), QVector::constEnd(), QVector::iterator, QVectorIterator -*/ - -/*! \typedef QVector::reverse_iterator - \since 5.6 - - The QVector::reverse_iterator typedef provides an STL-style non-const - reverse iterator for QVector. - - It is simply a typedef for \c{std::reverse_iterator}. - - \warning Iterators on implicitly shared containers do not work - exactly like STL-iterators. You should avoid copying a container - while iterators are active on that container. For more information, - read \l{Implicit sharing iterator problem}. - - \sa QVector::rbegin(), QVector::rend(), QVector::const_reverse_iterator, QVector::iterator -*/ - -/*! \typedef QVector::const_reverse_iterator - \since 5.6 - - The QVector::const_reverse_iterator typedef provides an STL-style const - reverse iterator for QVector. - - It is simply a typedef for \c{std::reverse_iterator}. - - \warning Iterators on implicitly shared containers do not work - exactly like STL-iterators. You should avoid copying a container - while iterators are active on that container. For more information, - read \l{Implicit sharing iterator problem}. - - \sa QVector::rbegin(), QVector::rend(), QVector::reverse_iterator, QVector::const_iterator -*/ - -/*! \typedef QVector::Iterator - - Qt-style synonym for QVector::iterator. -*/ - -/*! \typedef QVector::ConstIterator - - Qt-style synonym for QVector::const_iterator. -*/ - -/*! \typedef QVector::const_pointer - - Typedef for const T *. Provided for STL compatibility. -*/ - -/*! \typedef QVector::const_reference - - Typedef for T &. Provided for STL compatibility. -*/ - -/*! \typedef QVector::difference_type - - Typedef for ptrdiff_t. Provided for STL compatibility. -*/ - -/*! \typedef QVector::pointer - - Typedef for T *. Provided for STL compatibility. -*/ - -/*! \typedef QVector::reference - - Typedef for T &. Provided for STL compatibility. -*/ - -/*! \typedef QVector::size_type - - Typedef for int. Provided for STL compatibility. -*/ - -/*! \typedef QVector::value_type - - Typedef for T. Provided for STL compatibility. -*/ - -/*! \fn QList QVector::toList() const - - Returns a QList object with the data contained in this QVector. - - Example: - - \snippet code/src_corelib_tools_qvector.cpp 14 - - \sa fromList(), QList::fromVector() -*/ - -/*! \fn QVector QVector::fromList(const QList &list) - - Returns a QVector object with the data contained in \a list. - - Example: - - \snippet code/src_corelib_tools_qvector.cpp 15 - - \sa toList(), QList::toVector() -*/ - -/*! \fn QVector QVector::fromStdVector(const std::vector &vector) - - Returns a QVector object with the data contained in \a vector. The - order of the elements in the QVector is the same as in \a vector. - - Example: - - \snippet code/src_corelib_tools_qvector.cpp 16 - - \sa toStdVector(), QList::fromStdList() -*/ - -/*! \fn std::vector QVector::toStdVector() const - - Returns a std::vector object with the data contained in this QVector. - Example: - - \snippet code/src_corelib_tools_qvector.cpp 17 - - \sa fromStdVector(), QList::toStdList() -*/ - -/*! \fn QDataStream &operator<<(QDataStream &out, const QVector &vector) - \relates QVector - - Writes the vector \a vector to stream \a out. - - This function requires the value type to implement \c operator<<(). - - \sa{Serializing Qt Data Types}{Format of the QDataStream operators} -*/ - -/*! \fn QDataStream &operator>>(QDataStream &in, QVector &vector) - \relates QVector - - Reads a vector from stream \a in into \a vector. - - This function requires the value type to implement \c operator>>(). - - \sa{Serializing Qt Data Types}{Format of the QDataStream operators} -*/ diff --git a/src/corelib/tools/qvector.qdoc b/src/corelib/tools/qvector.qdoc new file mode 100644 index 0000000000..0ea47b1a1a --- /dev/null +++ b/src/corelib/tools/qvector.qdoc @@ -0,0 +1,1346 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \class QVector + \inmodule QtCore + \brief The QVector class is a template class that provides a dynamic array. + + \ingroup tools + \ingroup shared + + \reentrant + + QVector\ is one of Qt's generic \l{container classes}. It + stores its items in adjacent memory locations and provides fast + index-based access. + + QList\, QLinkedList\, QVector\, and QVarLengthArray\ + provide similar APIs and functionality. They are often interchangeable, + but there are performance consequences. Here is an overview of use cases: + + \list + \li QVector should be your default first choice. + QVector\ will usually give better performance than QList\, + because QVector\ always stores its items sequentially in memory, + where QList\ will allocate its items on the heap unless + \c {sizeof(T) <= sizeof(void*)} and T has been declared to be + either a \c{Q_MOVABLE_TYPE} or a \c{Q_PRIMITIVE_TYPE} using + \l {Q_DECLARE_TYPEINFO}. See the \l {Pros and Cons of Using QList} + for an explanation. + \li However, QList is used throughout the Qt APIs for passing + parameters and for returning values. Use QList to interface with + those APIs. + \li If you need a real linked list, which guarantees + \l{Algorithmic Complexity}{constant time} insertions mid-list and + uses iterators to items rather than indexes, use QLinkedList. + \endlist + + \note QVector and QVarLengthArray both guarantee C-compatible + array layout. QList does not. This might be important if your + application must interface with a C API. + + \note Iterators into a QLinkedList and references into + heap-allocating QLists remain valid as long as the referenced items + remain in the container. This is not true for iterators and + references into a QVector and non-heap-allocating QLists. + + Here's an example of a QVector that stores integers and a QVector + that stores QString values: + + \snippet code/src_corelib_tools_qvector.cpp 0 + + QVector stores its items in a vector (array). Typically, vectors + are created with an initial size. For example, the following code + constructs a QVector with 200 elements: + + \snippet code/src_corelib_tools_qvector.cpp 1 + + The elements are automatically initialized with a + \l{default-constructed value}. If you want to initialize the + vector with a different value, pass that value as the second + argument to the constructor: + + \snippet code/src_corelib_tools_qvector.cpp 2 + + You can also call fill() at any time to fill the vector with a + value. + + QVector uses 0-based indexes, just like C++ arrays. To access the + item at a particular index position, you can use operator[](). On + non-const vectors, operator[]() returns a reference to the item + that can be used on the left side of an assignment: + + \snippet code/src_corelib_tools_qvector.cpp 3 + + For read-only access, an alternative syntax is to use at(): + + \snippet code/src_corelib_tools_qvector.cpp 4 + + at() can be faster than operator[](), because it never causes a + \l{deep copy} to occur. + + Another way to access the data stored in a QVector is to call + data(). The function returns a pointer to the first item in the + vector. You can use the pointer to directly access and modify the + elements stored in the vector. The pointer is also useful if you + need to pass a QVector to a function that accepts a plain C++ + array. + + If you want to find all occurrences of a particular value in a + vector, use indexOf() or lastIndexOf(). The former searches + forward starting from a given index position, the latter searches + backward. Both return the index of the matching item if they found + one; otherwise, they return -1. For example: + + \snippet code/src_corelib_tools_qvector.cpp 5 + + If you simply want to check whether a vector contains a + particular value, use contains(). If you want to find out how + many times a particular value occurs in the vector, use count(). + + QVector provides these basic functions to add, move, and remove + items: insert(), replace(), remove(), prepend(), append(). With + the exception of append() and replace(), these functions can be slow + (\l{linear time}) for large vectors, because they require moving many + items in the vector by one position in memory. If you want a container + class that provides fast insertion/removal in the middle, use + QList or QLinkedList instead. + + Unlike plain C++ arrays, QVectors can be resized at any time by + calling resize(). If the new size is larger than the old size, + QVector might need to reallocate the whole vector. QVector tries + to reduce the number of reallocations by preallocating up to twice + as much memory as the actual data needs. + + If you know in advance approximately how many items the QVector + will contain, you can call reserve(), asking QVector to + preallocate a certain amount of memory. You can also call + capacity() to find out how much memory QVector actually + allocated. + + Note that using non-const operators and functions can cause + QVector to do a deep copy of the data. This is due to \l{implicit sharing}. + + QVector's value type must be an \l{assignable data type}. This + covers most data types that are commonly used, but the compiler + won't let you, for example, store a QWidget as a value; instead, + store a QWidget *. A few functions have additional requirements; + for example, indexOf() and lastIndexOf() expect the value type to + support \c operator==(). These requirements are documented on a + per-function basis. + + Like the other container classes, QVector provides \l{Java-style + iterators} (QVectorIterator and QMutableVectorIterator) and + \l{STL-style iterators} (QVector::const_iterator and + QVector::iterator). In practice, these are rarely used, because + you can use indexes into the QVector. + + In addition to QVector, Qt also provides QVarLengthArray, a very + low-level class with little functionality that is optimized for + speed. + + QVector does \e not support inserting, prepending, appending or replacing + with references to its own values. Doing so will cause your application to + abort with an error message. + + \section2 More Information on Using Qt Containers + + For a detailed discussion comparing Qt containers with each other and + with STL containers, see \l {Understand the Qt Containers}. + + \sa QVectorIterator, QMutableVectorIterator, QList, QLinkedList +*/ + +/*! + \fn QVector QVector::mid(int pos, int length = -1) const + + Returns a sub-vector which contains elements from this vector, + starting at position \a pos. If \a length is -1 (the default), all + elements after \a pos are included; otherwise \a length elements (or + all remaining elements if there are less than \a length elements) + are included. +*/ + + +/*! \fn QVector::QVector() + + Constructs an empty vector. + + \sa resize() +*/ + +/*! + \fn QVector::QVector(QVector &&other) + + Move-constructs a QVector instance, making it point at the same + object that \a other was pointing to. + + \since 5.2 +*/ + +/*! \fn QVector::QVector(int size) + + Constructs a vector with an initial size of \a size elements. + + The elements are initialized with a \l{default-constructed + value}. + + \sa resize() +*/ + +/*! \fn QVector::QVector(int size, const T &value) + + Constructs a vector with an initial size of \a size elements. + Each element is initialized with \a value. + + \sa resize(), fill() +*/ + +/*! \fn QVector::QVector(const QVector &other) + + Constructs a copy of \a other. + + This operation takes \l{Algorithmic Complexity}{constant time}, + because QVector is \l{implicitly shared}. This makes returning + a QVector from a function very fast. If a shared instance is + modified, it will be copied (copy-on-write), and that takes + \l{Algorithmic Complexity}{linear time}. + + \sa operator=() +*/ + +/*! \fn QVector::QVector(std::initializer_list args) + \since 4.8 + + Constructs a vector from the std::initializer_list given by \a args. + + This constructor is only enabled if the compiler supports C++11 initializer + lists. +*/ + + +/*! \fn QVector::~QVector() + + Destroys the vector. +*/ + +/*! \fn QVector &QVector::operator=(const QVector &other) + + Assigns \a other to this vector and returns a reference to this + vector. +*/ + +/*! + \fn QVector &QVector::operator=(QVector &&other) + + Move-assigns \a other to this QVector instance. + + \since 5.2 +*/ + +/*! \fn void QVector::swap(QVector &other) + \since 4.8 + + Swaps vector \a other with this vector. This operation is very fast and + never fails. +*/ + +/*! \fn bool QVector::operator==(const QVector &other) const + + Returns \c true if \a other is equal to this vector; otherwise + returns \c false. + + Two vectors are considered equal if they contain the same values + in the same order. + + This function requires the value type to have an implementation + of \c operator==(). + + \sa operator!=() +*/ + +/*! \fn bool QVector::operator!=(const QVector &other) const + + Returns \c true if \a other is not equal to this vector; otherwise + returns \c false. + + Two vectors are considered equal if they contain the same values + in the same order. + + This function requires the value type to have an implementation + of \c operator==(). + + \sa operator==() +*/ + +/*! \fn bool operator<(const QVector &lhs, const QVector &rhs) + \since 5.6 + \relates QVector + + Returns \c true if vector \a lhs is + \l{http://en.cppreference.com/w/cpp/algorithm/lexicographical_compare} + {lexicographically less than} \a rhs; otherwise returns \c false. + + This function requires the value type to have an implementation + of \c operator<(). +*/ + +/*! \fn bool operator<=(const QVector &lhs, const QVector &rhs) + \since 5.6 + \relates QVector + + Returns \c true if vector \a lhs is + \l{http://en.cppreference.com/w/cpp/algorithm/lexicographical_compare} + {lexicographically less than or equal to} \a rhs; otherwise returns \c false. + + This function requires the value type to have an implementation + of \c operator<(). +*/ + +/*! \fn bool operator>(const QVector &lhs, const QVector &rhs) + \since 5.6 + \relates QVector + + Returns \c true if vector \a lhs is + \l{http://en.cppreference.com/w/cpp/algorithm/lexicographical_compare} + {lexicographically greater than} \a rhs; otherwise returns \c false. + + This function requires the value type to have an implementation + of \c operator<(). +*/ + +/*! \fn bool operator>=(const QVector &lhs, const QVector &rhs) + \since 5.6 + \relates QVector + + Returns \c true if vector \a lhs is + \l{http://en.cppreference.com/w/cpp/algorithm/lexicographical_compare} + {lexicographically greater than or equal to} \a rhs; otherwise returns \c false. + + This function requires the value type to have an implementation + of \c operator<(). +*/ + +/*! + \fn uint qHash(const QVector &key, uint seed = 0) + \since 5.6 + \relates QVector + + Returns the hash value for \a key, + using \a seed to seed the calculation. + + This function requires qHash() to be overloaded for the value type \c T. +*/ + +/*! \fn int QVector::size() const + + Returns the number of items in the vector. + + \sa isEmpty(), resize() +*/ + +/*! \fn bool QVector::isEmpty() const + + Returns \c true if the vector has size 0; otherwise returns \c false. + + \sa size(), resize() +*/ + +/*! \fn void QVector::resize(int size) + + Sets the size of the vector to \a size. If \a size is greater than the + current size, elements are added to the end; the new elements are + initialized with a \l{default-constructed value}. If \a size is less + than the current size, elements are removed from the end. + + Since Qt 5.6, resize() doesn't shrink the capacity anymore. + To shed excess capacity, use squeeze(). + + \sa size() +*/ + +/*! \fn int QVector::capacity() const + + Returns the maximum number of items that can be stored in the + vector without forcing a reallocation. + + The sole purpose of this function is to provide a means of fine + tuning QVector's memory usage. In general, you will rarely ever + need to call this function. If you want to know how many items are + in the vector, call size(). + + \sa reserve(), squeeze() +*/ + +/*! \fn void QVector::reserve(int size) + + Attempts to allocate memory for at least \a size elements. If you + know in advance how large the vector will be, you should call this + function to prevent reallocations and memory fragmentation. + + If \a size is an underestimate, the worst that will happen is that + the QVector will be a bit slower. If \a size is an overestimate, you + may have used more memory than the normal QVector growth strategy + would have allocated—or you may have used less. + + An alternative to reserve() is calling resize(). Whether or not that is + faster than reserve() depends on the element type, because resize() + default-constructs all elements, and requires assignment to existing + entries rather than calling append(), which copy- or move-constructs. + For simple types, like \c int or \c double, resize() is typically faster, + but for anything more complex, you should prefer reserve(). + + \warning If the size passed to resize() was underestimated, you run out + of allocated space and into undefined behavior. This problem does not + exist with reserve(), because it treats the size as just a hint. + + \sa squeeze(), capacity() +*/ + +/*! \fn void QVector::squeeze() + + Releases any memory not required to store the items. + + The sole purpose of this function is to provide a means of fine + tuning QVector's memory usage. In general, you will rarely ever + need to call this function. + + \sa reserve(), capacity() +*/ + +/*! \fn void QVector::detach() + + \internal +*/ + +/*! \fn bool QVector::isDetached() const + + \internal +*/ + +/*! \fn void QVector::setSharable(bool sharable) + + \internal +*/ + +/*! \fn bool QVector::isSharedWith(const QVector &other) const + + \internal +*/ + +/*! \fn T *QVector::data() + + Returns a pointer to the data stored in the vector. The pointer + can be used to access and modify the items in the vector. + + Example: + \snippet code/src_corelib_tools_qvector.cpp 6 + + The pointer remains valid as long as the vector isn't + reallocated. + + This function is mostly useful to pass a vector to a function + that accepts a plain C++ array. + + \sa constData(), operator[]() +*/ + +/*! \fn const T *QVector::data() const + + \overload +*/ + +/*! \fn const T *QVector::constData() const + + Returns a const pointer to the data stored in the vector. The + pointer can be used to access the items in the vector. + The pointer remains valid as long as the vector isn't + reallocated. + + This function is mostly useful to pass a vector to a function + that accepts a plain C++ array. + + \sa data(), operator[]() +*/ + +/*! \fn void QVector::clear() + + Removes all the elements from the vector. + + \note Until Qt 5.6, this also released the memory used by + the vector. From Qt 5.7, the capacity is preserved. To shed + all capacity, swap with a default-constructed vector: + \code + QVector v ...; + QVector().swap(v); + Q_ASSERT(v.capacity() == 0); + \endcode + or call squeeze(). + + \sa squeeze() +*/ + +/*! \fn const T &QVector::at(int i) const + + Returns the item at index position \a i in the vector. + + \a i must be a valid index position in the vector (i.e., 0 <= \a + i < size()). + + \sa value(), operator[]() +*/ + +/*! \fn T &QVector::operator[](int i) + + Returns the item at index position \a i as a modifiable reference. + + \a i must be a valid index position in the vector (i.e., 0 <= \a i + < size()). + + Note that using non-const operators can cause QVector to do a deep + copy. + + \sa at(), value() +*/ + +/*! \fn const T &QVector::operator[](int i) const + + \overload + + Same as at(\a i). +*/ + +/*! + \fn void QVector::append(const T &value) + + Inserts \a value at the end of the vector. + + Example: + \snippet code/src_corelib_tools_qvector.cpp 7 + + This is the same as calling resize(size() + 1) and assigning \a + value to the new last element in the vector. + + This operation is relatively fast, because QVector typically + allocates more memory than necessary, so it can grow without + reallocating the entire vector each time. + + \sa operator<<(), prepend(), insert() +*/ + +/*! + \fn void QVector::append(T &&value) + \since 5.6 + + \overload + + Example: + \snippet code/src_corelib_tools_qvector.cpp move-append +*/ + +/*! \fn void QVector::append(const QVector &value) + + \overload + + \since 5.5 + + Appends the items of the \a value vector to this vector. + + \sa operator<<(), operator+=() +*/ + + +/*! \fn void QVector::prepend(const T &value) + + Inserts \a value at the beginning of the vector. + + Example: + \snippet code/src_corelib_tools_qvector.cpp 8 + + This is the same as vector.insert(0, \a value). + + For large vectors, this operation can be slow (\l{linear time}), + because it requires moving all the items in the vector by one + position further in memory. If you want a container class that + provides a fast prepend() function, use QList or QLinkedList + instead. + + \sa append(), insert() +*/ + +/*! \fn void QVector::insert(int i, const T &value) + + Inserts \a value at index position \a i in the vector. If \a i is + 0, the value is prepended to the vector. If \a i is size(), the + value is appended to the vector. + + Example: + \snippet code/src_corelib_tools_qvector.cpp 9 + + For large vectors, this operation can be slow (\l{linear time}), + because it requires moving all the items at indexes \a i and + above by one position further in memory. If you want a container + class that provides a fast insert() function, use QLinkedList + instead. + + \sa append(), prepend(), remove() +*/ + +/*! \fn void QVector::insert(int i, int count, const T &value) + + \overload + + Inserts \a count copies of \a value at index position \a i in the + vector. + + Example: + \snippet code/src_corelib_tools_qvector.cpp 10 +*/ + +/*! \fn QVector::iterator QVector::insert(iterator before, const T &value) + + \overload + + Inserts \a value in front of the item pointed to by the iterator + \a before. Returns an iterator pointing at the inserted item. +*/ + +/*! \fn QVector::iterator QVector::insert(iterator before, int count, const T &value) + + Inserts \a count copies of \a value in front of the item pointed to + by the iterator \a before. Returns an iterator pointing at the + first of the inserted items. +*/ + +/*! \fn void QVector::replace(int i, const T &value) + + Replaces the item at index position \a i with \a value. + + \a i must be a valid index position in the vector (i.e., 0 <= \a + i < size()). + + \sa operator[](), remove() +*/ + +/*! \fn void QVector::remove(int i) + + \overload + + Removes the element at index position \a i. + + \sa insert(), replace(), fill() +*/ + +/*! \fn void QVector::remove(int i, int count) + + \overload + + Removes \a count elements from the middle of the vector, starting at + index position \a i. + + \sa insert(), replace(), fill() +*/ + +/*! \fn void QVector::removeAt(int i) + \since 5.2 + + Removes the element at index position \a i. + Equivalent to + \code + remove(i); + \endcode + + Provided for compatibility with QList. + + \sa remove(), QList::removeAt() +*/ + +/*! \fn int QVector::removeAll(const T &t) + \since 5.4 + + Removes all elements that compare equal to \a t from the + vector. Returns the number of elements removed, if any. + + Provided for compatibility with QList. + + \sa removeOne(), QList::removeAll() +*/ + +/*! \fn bool QVector::removeOne(const T &t) + \since 5.4 + + Removes the first element that compares equal to \a t from the + vector. Returns whether an element was, in fact, removed. + + Provided for compatibility with QList. + + \sa removeAll(), QList::removeOne() +*/ + +/*! \fn int QVector::length() const + \since 5.2 + + Same as size() and count(). + + Provided for compatibility with QList. + + \sa size(), count(), QList::length() +*/ + +/*! \fn T QVector::takeAt(int i) + \since 5.2 + + Removes the element at index position \a i and returns it. + + Equivalent to + \code + T t = at(i); + remove(i); + return t; + \endcode + + Provided for compatibility with QList. + + \sa takeFirst(), takeLast(), QList::takeAt() +*/ + +/*! \fn void QVector::move(int from, int to) + \since 5.6 + + Moves the item at index position \a from to index position \a to. + + Provided for compatibility with QList. + + \sa QList::move() +*/ + +/*! \fn void QVector::removeFirst() + \since 5.1 + Removes the first item in the vector. Calling this function is + equivalent to calling remove(0). The vector must not be empty. If + the vector can be empty, call isEmpty() before calling this + function. + + \sa remove(), takeFirst(), isEmpty() +*/ + +/*! \fn void QVector::removeLast() + \since 5.1 + Removes the last item in the vector. Calling this function is + equivalent to calling remove(size() - 1). The vector must not be + empty. If the vector can be empty, call isEmpty() before calling + this function. + + \sa remove(), takeLast(), removeFirst(), isEmpty() +*/ + +/*! \fn T QVector::takeFirst() + \since 5.1 + + Removes the first item in the vector and returns it. This function + assumes the vector is not empty. To avoid failure, call isEmpty() + before calling this function. + + \sa takeLast(), removeFirst() +*/ + +/*! \fn T QVector::takeLast() + \since 5.1 + + Removes the last item in the list and returns it. This function + assumes the vector is not empty. To avoid failure, call isEmpty() + before calling this function. + + If you don't use the return value, removeLast() is more + efficient. + + \sa takeFirst(), removeLast() +*/ + + +/*! \fn QVector &QVector::fill(const T &value, int size = -1) + + Assigns \a value to all items in the vector. If \a size is + different from -1 (the default), the vector is resized to size \a + size beforehand. + + Example: + \snippet code/src_corelib_tools_qvector.cpp 11 + + \sa resize() +*/ + +/*! \fn int QVector::indexOf(const T &value, int from = 0) const + + Returns the index position of the first occurrence of \a value in + the vector, searching forward from index position \a from. + Returns -1 if no item matched. + + Example: + \snippet code/src_corelib_tools_qvector.cpp 12 + + This function requires the value type to have an implementation of + \c operator==(). + + \sa lastIndexOf(), contains() +*/ + +/*! \fn int QVector::lastIndexOf(const T &value, int from = -1) const + + Returns the index position of the last occurrence of the value \a + value in the vector, searching backward from index position \a + from. If \a from is -1 (the default), the search starts at the + last item. Returns -1 if no item matched. + + Example: + \snippet code/src_corelib_tools_qvector.cpp 13 + + This function requires the value type to have an implementation of + \c operator==(). + + \sa indexOf() +*/ + +/*! \fn bool QVector::contains(const T &value) const + + Returns \c true if the vector contains an occurrence of \a value; + otherwise returns \c false. + + This function requires the value type to have an implementation of + \c operator==(). + + \sa indexOf(), count() +*/ + +/*! \fn bool QVector::startsWith(const T &value) const + \since 4.5 + + Returns \c true if this vector is not empty and its first + item is equal to \a value; otherwise returns \c false. + + \sa isEmpty(), first() +*/ + +/*! \fn bool QVector::endsWith(const T &value) const + \since 4.5 + + Returns \c true if this vector is not empty and its last + item is equal to \a value; otherwise returns \c false. + + \sa isEmpty(), last() +*/ + + +/*! \fn int QVector::count(const T &value) const + + Returns the number of occurrences of \a value in the vector. + + This function requires the value type to have an implementation of + \c operator==(). + + \sa contains(), indexOf() +*/ + +/*! \fn int QVector::count() const + + \overload + + Same as size(). +*/ + +/*! \fn QVector::iterator QVector::begin() + + Returns an \l{STL-style iterators}{STL-style iterator} pointing to the first item in + the vector. + + \sa constBegin(), end() +*/ + +/*! \fn QVector::const_iterator QVector::begin() const + + \overload +*/ + +/*! \fn QVector::const_iterator QVector::cbegin() const + \since 5.0 + + Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first item + in the vector. + + \sa begin(), cend() +*/ + +/*! \fn QVector::const_iterator QVector::constBegin() const + + Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first item + in the vector. + + \sa begin(), constEnd() +*/ + +/*! \fn QVector::iterator QVector::end() + + Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item + after the last item in the vector. + + \sa begin(), constEnd() +*/ + +/*! \fn QVector::const_iterator QVector::end() const + + \overload +*/ + +/*! \fn QVector::const_iterator QVector::cend() const + \since 5.0 + + Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary + item after the last item in the vector. + + \sa cbegin(), end() +*/ + +/*! \fn QVector::const_iterator QVector::constEnd() const + + Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary + item after the last item in the vector. + + \sa constBegin(), end() +*/ + +/*! \fn QVector::reverse_iterator QVector::rbegin() + \since 5.6 + + Returns a \l{STL-style iterators}{STL-style} reverse iterator pointing to the first + item in the vector, in reverse order. + + \sa begin(), crbegin(), rend() +*/ + +/*! \fn QVector::const_reverse_iterator QVector::rbegin() const + \since 5.6 + \overload +*/ + +/*! \fn QVector::const_reverse_iterator QVector::crbegin() const + \since 5.6 + + Returns a const \l{STL-style iterators}{STL-style} reverse iterator pointing to the first + item in the vector, in reverse order. + + \sa begin(), rbegin(), rend() +*/ + +/*! \fn QVector::reverse_iterator QVector::rend() + \since 5.6 + + Returns a \l{STL-style iterators}{STL-style} reverse iterator pointing to one past + the last item in the vector, in reverse order. + + \sa end(), crend(), rbegin() +*/ + +/*! \fn QVector::const_reverse_iterator QVector::rend() const + \since 5.6 + \overload +*/ + +/*! \fn QVector::const_reverse_iterator QVector::crend() const + \since 5.6 + + Returns a const \l{STL-style iterators}{STL-style} reverse iterator pointing to one + past the last item in the vector, in reverse order. + + \sa end(), rend(), rbegin() +*/ + +/*! \fn QVector::iterator QVector::erase(iterator pos) + + Removes the item pointed to by the iterator \a pos from the + vector, and returns an iterator to the next item in the vector + (which may be end()). + + \sa insert(), remove() +*/ + +/*! \fn QVector::iterator QVector::erase(iterator begin, iterator end) + + \overload + + Removes all the items from \a begin up to (but not including) \a + end. Returns an iterator to the same item that \a end referred to + before the call. +*/ + +/*! \fn T& QVector::first() + + Returns a reference to the first item in the vector. This + function assumes that the vector isn't empty. + + \sa last(), isEmpty(), constFirst() +*/ + +/*! \fn const T& QVector::first() const + + \overload +*/ + +/*! \fn const T& QVector::constFirst() const + \since 5.6 + + Returns a const reference to the first item in the vector. This + function assumes that the vector isn't empty. + + \sa constLast(), isEmpty(), first() +*/ + +/*! \fn T& QVector::last() + + Returns a reference to the last item in the vector. This function + assumes that the vector isn't empty. + + \sa first(), isEmpty(), constLast() +*/ + +/*! \fn const T& QVector::last() const + + \overload +*/ + +/*! \fn const T& QVector::constLast() const + \since 5.6 + + Returns a const reference to the last item in the vector. This function + assumes that the vector isn't empty. + + \sa constFirst(), isEmpty(), last() +*/ + +/*! \fn T QVector::value(int i) const + + Returns the value at index position \a i in the vector. + + If the index \a i is out of bounds, the function returns + a \l{default-constructed value}. If you are certain that + \a i is within bounds, you can use at() instead, which is slightly + faster. + + \sa at(), operator[]() +*/ + +/*! \fn T QVector::value(int i, const T &defaultValue) const + + \overload + + If the index \a i is out of bounds, the function returns + \a defaultValue. +*/ + +/*! \fn void QVector::push_back(const T &value) + + This function is provided for STL compatibility. It is equivalent + to append(\a value). +*/ + +/*! \fn void QVector::push_back(T &&value) + \since 5.6 + \overload +*/ + +/*! \fn void QVector::push_front(const T &value) + + This function is provided for STL compatibility. It is equivalent + to prepend(\a value). +*/ + +/*! \fn void QVector::pop_front() + + This function is provided for STL compatibility. It is equivalent + to removeFirst(). +*/ + +/*! \fn void QVector::pop_back() + + This function is provided for STL compatibility. It is equivalent + to removeLast(). +*/ + +/*! \fn T& QVector::front() + + This function is provided for STL compatibility. It is equivalent + to first(). +*/ + +/*! \fn QVector::const_reference QVector::front() const + + \overload +*/ + +/*! \fn QVector::reference QVector::back() + + This function is provided for STL compatibility. It is equivalent + to last(). +*/ + +/*! \fn QVector::const_reference QVector::back() const + + \overload +*/ + +/*! \fn bool QVector::empty() const + + This function is provided for STL compatibility. It is equivalent + to isEmpty(), returning \c true if the vector is empty; otherwise + returns \c false. +*/ + +/*! \fn QVector &QVector::operator+=(const QVector &other) + + Appends the items of the \a other vector to this vector and + returns a reference to this vector. + + \sa operator+(), append() +*/ + +/*! \fn void QVector::operator+=(const T &value) + + \overload + + Appends \a value to the vector. + + \sa append(), operator<<() +*/ + +/*! \fn QVector QVector::operator+(const QVector &other) const + + Returns a vector that contains all the items in this vector + followed by all the items in the \a other vector. + + \sa operator+=() +*/ + +/*! \fn QVector &QVector::operator<<(const T &value) + + Appends \a value to the vector and returns a reference to this + vector. + + \sa append(), operator+=() +*/ + +/*! \fn QVector &QVector::operator<<(const QVector &other) + + Appends \a other to the vector and returns a reference to the + vector. +*/ + +/*! \typedef QVector::iterator + + The QVector::iterator typedef provides an STL-style non-const + iterator for QVector and QStack. + + QVector provides both \l{STL-style iterators} and \l{Java-style + iterators}. The STL-style non-const iterator is simply a typedef + for "T *" (pointer to T). + + \warning Iterators on implicitly shared containers do not work + exactly like STL-iterators. You should avoid copying a container + while iterators are active on that container. For more information, + read \l{Implicit sharing iterator problem}. + + \sa QVector::begin(), QVector::end(), QVector::const_iterator, QMutableVectorIterator +*/ + +/*! \typedef QVector::const_iterator + + The QVector::const_iterator typedef provides an STL-style const + iterator for QVector and QStack. + + QVector provides both \l{STL-style iterators} and \l{Java-style + iterators}. The STL-style const iterator is simply a typedef for + "const T *" (pointer to const T). + + \warning Iterators on implicitly shared containers do not work + exactly like STL-iterators. You should avoid copying a container + while iterators are active on that container. For more information, + read \l{Implicit sharing iterator problem}. + + \sa QVector::constBegin(), QVector::constEnd(), QVector::iterator, QVectorIterator +*/ + +/*! \typedef QVector::reverse_iterator + \since 5.6 + + The QVector::reverse_iterator typedef provides an STL-style non-const + reverse iterator for QVector. + + It is simply a typedef for \c{std::reverse_iterator}. + + \warning Iterators on implicitly shared containers do not work + exactly like STL-iterators. You should avoid copying a container + while iterators are active on that container. For more information, + read \l{Implicit sharing iterator problem}. + + \sa QVector::rbegin(), QVector::rend(), QVector::const_reverse_iterator, QVector::iterator +*/ + +/*! \typedef QVector::const_reverse_iterator + \since 5.6 + + The QVector::const_reverse_iterator typedef provides an STL-style const + reverse iterator for QVector. + + It is simply a typedef for \c{std::reverse_iterator}. + + \warning Iterators on implicitly shared containers do not work + exactly like STL-iterators. You should avoid copying a container + while iterators are active on that container. For more information, + read \l{Implicit sharing iterator problem}. + + \sa QVector::rbegin(), QVector::rend(), QVector::reverse_iterator, QVector::const_iterator +*/ + +/*! \typedef QVector::Iterator + + Qt-style synonym for QVector::iterator. +*/ + +/*! \typedef QVector::ConstIterator + + Qt-style synonym for QVector::const_iterator. +*/ + +/*! \typedef QVector::const_pointer + + Typedef for const T *. Provided for STL compatibility. +*/ + +/*! \typedef QVector::const_reference + + Typedef for T &. Provided for STL compatibility. +*/ + +/*! \typedef QVector::difference_type + + Typedef for ptrdiff_t. Provided for STL compatibility. +*/ + +/*! \typedef QVector::pointer + + Typedef for T *. Provided for STL compatibility. +*/ + +/*! \typedef QVector::reference + + Typedef for T &. Provided for STL compatibility. +*/ + +/*! \typedef QVector::size_type + + Typedef for int. Provided for STL compatibility. +*/ + +/*! \typedef QVector::value_type + + Typedef for T. Provided for STL compatibility. +*/ + +/*! \fn QList QVector::toList() const + + Returns a QList object with the data contained in this QVector. + + Example: + + \snippet code/src_corelib_tools_qvector.cpp 14 + + \sa fromList(), QList::fromVector() +*/ + +/*! \fn QVector QVector::fromList(const QList &list) + + Returns a QVector object with the data contained in \a list. + + Example: + + \snippet code/src_corelib_tools_qvector.cpp 15 + + \sa toList(), QList::toVector() +*/ + +/*! \fn QVector QVector::fromStdVector(const std::vector &vector) + + Returns a QVector object with the data contained in \a vector. The + order of the elements in the QVector is the same as in \a vector. + + Example: + + \snippet code/src_corelib_tools_qvector.cpp 16 + + \sa toStdVector(), QList::fromStdList() +*/ + +/*! \fn std::vector QVector::toStdVector() const + + Returns a std::vector object with the data contained in this QVector. + Example: + + \snippet code/src_corelib_tools_qvector.cpp 17 + + \sa fromStdVector(), QList::toStdList() +*/ + +/*! \fn QDataStream &operator<<(QDataStream &out, const QVector &vector) + \relates QVector + + Writes the vector \a vector to stream \a out. + + This function requires the value type to implement \c operator<<(). + + \sa{Serializing Qt Data Types}{Format of the QDataStream operators} +*/ + +/*! \fn QDataStream &operator>>(QDataStream &in, QVector &vector) + \relates QVector + + Reads a vector from stream \a in into \a vector. + + This function requires the value type to implement \c operator>>(). + + \sa{Serializing Qt Data Types}{Format of the QDataStream operators} +*/ diff --git a/src/corelib/tools/tools.pri b/src/corelib/tools/tools.pri index fa8e07abbc..1c9b34dacd 100644 --- a/src/corelib/tools/tools.pri +++ b/src/corelib/tools/tools.pri @@ -110,7 +110,6 @@ SOURCES += \ tools/qtextboundaryfinder.cpp \ tools/qtimeline.cpp \ tools/qunicodetools.cpp \ - tools/qvector.cpp \ tools/qvsnprintf.cpp \ tools/qversionnumber.cpp diff --git a/src/tools/bootstrap/bootstrap.pro b/src/tools/bootstrap/bootstrap.pro index d51f9e98a4..2acc3fc77c 100644 --- a/src/tools/bootstrap/bootstrap.pro +++ b/src/tools/bootstrap/bootstrap.pro @@ -78,7 +78,6 @@ SOURCES += \ ../../corelib/tools/qstringbuilder.cpp \ ../../corelib/tools/qstring_compat.cpp \ ../../corelib/tools/qstringlist.cpp \ - ../../corelib/tools/qvector.cpp \ ../../corelib/tools/qvsnprintf.cpp \ ../../corelib/xml/qxmlutils.cpp \ ../../corelib/xml/qxmlstream.cpp \ -- cgit v1.2.3 From 60563855e589cb51b4966ca51a4d390a2f1609a4 Mon Sep 17 00:00:00 2001 From: Alex Trotsenko Date: Fri, 21 Oct 2016 18:52:54 +0300 Subject: Refactor QIODevice::read() Move device-dependent part of the code into the private function for further reusing by peek() procedure. Task-number: QTBUG-56032 Change-Id: Iedceafe4b0bab109ca5c64ad274d779efe87c27b Reviewed-by: Edward Welbourne --- src/corelib/io/qiodevice.cpp | 86 +++++++++++++++++++++++++------------------- src/corelib/io/qiodevice_p.h | 1 + 2 files changed, 51 insertions(+), 36 deletions(-) diff --git a/src/corelib/io/qiodevice.cpp b/src/corelib/io/qiodevice.cpp index 52a78ad1c4..8cef993cf4 100644 --- a/src/corelib/io/qiodevice.cpp +++ b/src/corelib/io/qiodevice.cpp @@ -1007,10 +1007,9 @@ qint64 QIODevice::read(char *data, qint64 maxSize) #endif const bool sequential = d->isSequential(); - const bool keepDataInBuffer = sequential && d->transactionStarted; - // Short circuit for getChar() - if (maxSize == 1 && !keepDataInBuffer) { + // Short-cut for getChar(), unless we need to keep the data in the buffer. + if (maxSize == 1 && !(sequential && d->transactionStarted)) { int chint; while ((chint = d->buffer.getChar()) != -1) { if (!sequential) @@ -1031,6 +1030,30 @@ qint64 QIODevice::read(char *data, qint64 maxSize) } CHECK_MAXLEN(read, qint64(-1)); + CHECK_READABLE(read, qint64(-1)); + + const qint64 readBytes = d->read(data, maxSize); + +#if defined QIODEVICE_DEBUG + printf("%p \treturning %lld, d->pos == %lld, d->buffer.size() == %lld\n", this, + readBytes, d->pos, d->buffer.size()); + if (readBytes > 0) + debugBinaryString(data - readBytes, readBytes); +#endif + + return readBytes; +} + +/*! + \internal +*/ +qint64 QIODevicePrivate::read(char *data, qint64 maxSize) +{ + Q_Q(QIODevice); + + const bool buffered = (openMode & QIODevice::Unbuffered) == 0; + const bool sequential = isSequential(); + const bool keepDataInBuffer = sequential && transactionStarted; qint64 readSoFar = 0; bool madeBufferReadsOnly = true; bool deviceAtEof = false; @@ -1038,36 +1061,33 @@ qint64 QIODevice::read(char *data, qint64 maxSize) forever { // Try reading from the buffer. qint64 bufferReadChunkSize = keepDataInBuffer - ? d->buffer.peek(data, maxSize, d->transactionPos) - : d->buffer.read(data, maxSize); + ? buffer.peek(data, maxSize, transactionPos) + : buffer.read(data, maxSize); if (bufferReadChunkSize > 0) { if (keepDataInBuffer) - d->transactionPos += bufferReadChunkSize; + transactionPos += bufferReadChunkSize; else if (!sequential) - d->pos += bufferReadChunkSize; + pos += bufferReadChunkSize; +#if defined QIODEVICE_DEBUG + printf("%p \treading %lld bytes from buffer into position %lld\n", q, + bufferReadChunkSize, readSoFar); +#endif readSoFar += bufferReadChunkSize; data += bufferReadChunkSize; maxSize -= bufferReadChunkSize; -#if defined QIODEVICE_DEBUG - printf("%p \treading %lld bytes from buffer into position %lld\n", this, - bufferReadChunkSize, readSoFar - bufferReadChunkSize); -#endif - } else { - CHECK_READABLE(read, qint64(-1)); } if (maxSize > 0 && !deviceAtEof) { qint64 readFromDevice = 0; // Make sure the device is positioned correctly. - if (sequential || d->pos == d->devicePos || seek(d->pos)) { + if (sequential || pos == devicePos || q->seek(pos)) { madeBufferReadsOnly = false; // fix readData attempt - if ((maxSize >= d->readBufferChunkSize || (d->openMode & Unbuffered)) - && !keepDataInBuffer) { + if ((!buffered || maxSize >= readBufferChunkSize) && !keepDataInBuffer) { // Read big chunk directly to output buffer - readFromDevice = readData(data, maxSize); + readFromDevice = q->readData(data, maxSize); deviceAtEof = (readFromDevice != maxSize); #if defined QIODEVICE_DEBUG - printf("%p \treading %lld bytes from device (total %lld)\n", this, + printf("%p \treading %lld bytes from device (total %lld)\n", q, readFromDevice, readSoFar); #endif if (readFromDevice > 0) { @@ -1075,24 +1095,24 @@ qint64 QIODevice::read(char *data, qint64 maxSize) data += readFromDevice; maxSize -= readFromDevice; if (!sequential) { - d->pos += readFromDevice; - d->devicePos += readFromDevice; + pos += readFromDevice; + devicePos += readFromDevice; } } } else { // Do not read more than maxSize on unbuffered devices - const qint64 bytesToBuffer = (d->openMode & Unbuffered) - ? qMin(maxSize, qint64(d->readBufferChunkSize)) - : qint64(d->readBufferChunkSize); + const qint64 bytesToBuffer = (buffered || readBufferChunkSize < maxSize) + ? qint64(readBufferChunkSize) + : maxSize; // Try to fill QIODevice buffer by single read - readFromDevice = readData(d->buffer.reserve(bytesToBuffer), bytesToBuffer); + readFromDevice = q->readData(buffer.reserve(bytesToBuffer), bytesToBuffer); deviceAtEof = (readFromDevice != bytesToBuffer); - d->buffer.chop(bytesToBuffer - qMax(Q_INT64_C(0), readFromDevice)); + buffer.chop(bytesToBuffer - qMax(Q_INT64_C(0), readFromDevice)); if (readFromDevice > 0) { if (!sequential) - d->devicePos += readFromDevice; + devicePos += readFromDevice; #if defined QIODEVICE_DEBUG - printf("%p \treading %lld from device into buffer\n", this, + printf("%p \treading %lld from device into buffer\n", q, readFromDevice); #endif continue; @@ -1108,7 +1128,7 @@ qint64 QIODevice::read(char *data, qint64 maxSize) } } - if ((d->openMode & Text) && readPtr < data) { + if ((openMode & QIODevice::Text) && readPtr < data) { const char *endPtr = data; // optimization to avoid initial self-assignment @@ -1140,14 +1160,8 @@ qint64 QIODevice::read(char *data, qint64 maxSize) break; } -#if defined QIODEVICE_DEBUG - printf("%p \treturning %lld, d->pos == %lld, d->buffer.size() == %lld\n", this, - readSoFar, d->pos, d->buffer.size()); - debugBinaryString(data - readSoFar, readSoFar); -#endif - - if (madeBufferReadsOnly && d->isBufferEmpty()) - readData(data, 0); + if (madeBufferReadsOnly && isBufferEmpty()) + q->readData(data, 0); return readSoFar; } diff --git a/src/corelib/io/qiodevice_p.h b/src/corelib/io/qiodevice_p.h index 76bec89ef2..0e424b6831 100644 --- a/src/corelib/io/qiodevice_p.h +++ b/src/corelib/io/qiodevice_p.h @@ -171,6 +171,7 @@ public: void setReadChannelCount(int count); void setWriteChannelCount(int count); + qint64 read(char *data, qint64 maxSize); virtual qint64 peek(char *data, qint64 maxSize); virtual QByteArray peek(qint64 maxSize); -- cgit v1.2.3 From 6e8fcab7e07717526c8ea6eac8785bf27fa090c3 Mon Sep 17 00:00:00 2001 From: Alex Trotsenko Date: Sat, 17 Sep 2016 16:43:02 +0300 Subject: Improve QIODevice::peek() performance on buffered devices Since 5.7, QIODevice::peek() implementation is based on transaction mechanism. While technically it's correct, seeking backward on a buffered random-access device clears the internal buffer that affects the performance of reading. To solve the problem, this patch implements peek mode directly inside the reading procedure. Task-number: QTBUG-56032 Change-Id: Ic5269f76e44c491a0309e13aba87fa7cf7b9259f Reviewed-by: Edward Welbourne --- src/corelib/io/qiodevice.cpp | 83 ++++++++++++-------------- src/corelib/io/qiodevice_p.h | 2 +- tests/benchmarks/corelib/io/qiodevice/main.cpp | 32 ++++++++++ 3 files changed, 72 insertions(+), 45 deletions(-) diff --git a/src/corelib/io/qiodevice.cpp b/src/corelib/io/qiodevice.cpp index 8cef993cf4..41a4d7a1ba 100644 --- a/src/corelib/io/qiodevice.cpp +++ b/src/corelib/io/qiodevice.cpp @@ -1047,26 +1047,29 @@ qint64 QIODevice::read(char *data, qint64 maxSize) /*! \internal */ -qint64 QIODevicePrivate::read(char *data, qint64 maxSize) +qint64 QIODevicePrivate::read(char *data, qint64 maxSize, bool peeking) { Q_Q(QIODevice); const bool buffered = (openMode & QIODevice::Unbuffered) == 0; const bool sequential = isSequential(); - const bool keepDataInBuffer = sequential && transactionStarted; + const bool keepDataInBuffer = sequential + ? peeking || transactionStarted + : peeking && buffered; + const qint64 savedPos = pos; qint64 readSoFar = 0; bool madeBufferReadsOnly = true; bool deviceAtEof = false; char *readPtr = data; + qint64 bufferPos = (sequential && transactionStarted) ? transactionPos : Q_INT64_C(0); forever { // Try reading from the buffer. qint64 bufferReadChunkSize = keepDataInBuffer - ? buffer.peek(data, maxSize, transactionPos) + ? buffer.peek(data, maxSize, bufferPos) : buffer.read(data, maxSize); if (bufferReadChunkSize > 0) { - if (keepDataInBuffer) - transactionPos += bufferReadChunkSize; - else if (!sequential) + bufferPos += bufferReadChunkSize; + if (!sequential) pos += bufferReadChunkSize; #if defined QIODEVICE_DEBUG printf("%p \treading %lld bytes from buffer into position %lld\n", q, @@ -1160,6 +1163,16 @@ qint64 QIODevicePrivate::read(char *data, qint64 maxSize) break; } + // Restore positions after reading + if (keepDataInBuffer) { + if (peeking) + pos = savedPos; // does nothing on sequential devices + else + transactionPos = bufferPos; + } else if (peeking) { + seekBuffer(savedPos); // unbuffered random-access device + } + if (madeBufferReadsOnly && isBufferEmpty()) q->readData(data, 0); @@ -1773,27 +1786,7 @@ bool QIODevicePrivate::putCharHelper(char c) */ qint64 QIODevicePrivate::peek(char *data, qint64 maxSize) { - Q_Q(QIODevice); - - if (transactionStarted) { - const qint64 savedTransactionPos = transactionPos; - const qint64 savedPos = pos; - - qint64 readBytes = q->read(data, maxSize); - - // Restore initial position - if (isSequential()) - transactionPos = savedTransactionPos; - else - seekBuffer(savedPos); - return readBytes; - } - - q->startTransaction(); - qint64 readBytes = q->read(data, maxSize); - q->rollbackTransaction(); - - return readBytes; + return read(data, maxSize, true); } /*! @@ -1801,26 +1794,17 @@ qint64 QIODevicePrivate::peek(char *data, qint64 maxSize) */ QByteArray QIODevicePrivate::peek(qint64 maxSize) { - Q_Q(QIODevice); + QByteArray result(maxSize, Qt::Uninitialized); - if (transactionStarted) { - const qint64 savedTransactionPos = transactionPos; - const qint64 savedPos = pos; + const qint64 readBytes = read(result.data(), maxSize, true); - QByteArray result = q->read(maxSize); - - // Restore initial position - if (isSequential()) - transactionPos = savedTransactionPos; + if (readBytes < maxSize) { + if (readBytes <= 0) + result.clear(); else - seekBuffer(savedPos); - return result; + result.resize(readBytes); } - q->startTransaction(); - QByteArray result = q->read(maxSize); - q->rollbackTransaction(); - return result; } @@ -1858,7 +1842,12 @@ bool QIODevice::getChar(char *c) */ qint64 QIODevice::peek(char *data, qint64 maxSize) { - return d_func()->peek(data, maxSize); + Q_D(QIODevice); + + CHECK_MAXLEN(peek, qint64(-1)); + CHECK_READABLE(peek, qint64(-1)); + + return d->peek(data, maxSize); } /*! @@ -1880,7 +1869,13 @@ qint64 QIODevice::peek(char *data, qint64 maxSize) */ QByteArray QIODevice::peek(qint64 maxSize) { - return d_func()->peek(maxSize); + Q_D(QIODevice); + + CHECK_MAXLEN(peek, QByteArray()); + CHECK_MAXBYTEARRAYSIZE(peek); + CHECK_READABLE(peek, QByteArray()); + + return d->peek(maxSize); } /*! diff --git a/src/corelib/io/qiodevice_p.h b/src/corelib/io/qiodevice_p.h index 0e424b6831..71a326dd53 100644 --- a/src/corelib/io/qiodevice_p.h +++ b/src/corelib/io/qiodevice_p.h @@ -171,7 +171,7 @@ public: void setReadChannelCount(int count); void setWriteChannelCount(int count); - qint64 read(char *data, qint64 maxSize); + qint64 read(char *data, qint64 maxSize, bool peeking = false); virtual qint64 peek(char *data, qint64 maxSize); virtual QByteArray peek(qint64 maxSize); diff --git a/tests/benchmarks/corelib/io/qiodevice/main.cpp b/tests/benchmarks/corelib/io/qiodevice/main.cpp index b106a9fd3d..de4660a253 100644 --- a/tests/benchmarks/corelib/io/qiodevice/main.cpp +++ b/tests/benchmarks/corelib/io/qiodevice/main.cpp @@ -39,6 +39,8 @@ class tst_qiodevice : public QObject private slots: void read_old(); void read_old_data() { read_data(); } + void peekAndRead(); + void peekAndRead_data() { read_data(); } //void read_new(); //void read_new_data() { read_data(); } private: @@ -86,6 +88,36 @@ void tst_qiodevice::read_old() } } +void tst_qiodevice::peekAndRead() +{ + QFETCH(qint64, size); + + QString name = "tmp" + QString::number(size); + + { + QFile file(name); + file.open(QIODevice::WriteOnly); + file.seek(size); + file.write("x", 1); + file.close(); + } + + QBENCHMARK { + QFile file(name); + file.open(QIODevice::ReadOnly); + + QByteArray ba(size / 1024, Qt::Uninitialized); + while (!file.atEnd()) { + file.peek(ba.data(), ba.size()); + file.read(ba.data(), ba.size()); + } + } + + { + QFile file(name); + file.remove(); + } +} QTEST_MAIN(tst_qiodevice) -- cgit v1.2.3 From 68b21690e25533e0c35f4e29f6b373fb09b117a3 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Mon, 6 Feb 2017 15:50:17 -0800 Subject: Use QBasicMutex in qthread_win.cpp It's QtCore, so we're fine. Change-Id: Ifaee7464122d402991b6fffd14a0d7ad9c09e2f2 Reviewed-by: Friedemann Kleint Reviewed-by: Olivier Goffart (Woboq GmbH) --- src/corelib/thread/qthread_win.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/corelib/thread/qthread_win.cpp b/src/corelib/thread/qthread_win.cpp index a14c193bad..e6c70ecb55 100644 --- a/src/corelib/thread/qthread_win.cpp +++ b/src/corelib/thread/qthread_win.cpp @@ -96,7 +96,7 @@ void qt_create_tls() { if (qt_current_thread_data_tls_index != TLS_OUT_OF_INDEXES) return; - static QMutex mutex; + static QBasicMutex mutex; QMutexLocker locker(&mutex); qt_current_thread_data_tls_index = TlsAlloc(); } @@ -166,7 +166,7 @@ void QAdoptedThread::init() static QVector qt_adopted_thread_handles; static QVector qt_adopted_qthreads; -static QMutex qt_adopted_thread_watcher_mutex; +static QBasicMutex qt_adopted_thread_watcher_mutex; static DWORD qt_adopted_thread_watcher_id = 0; static HANDLE qt_adopted_thread_wakeup = 0; -- cgit v1.2.3 From b0c1e07d648caf585d2be12cf3d18eb42b86f721 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Tue, 7 Feb 2017 13:10:38 +0100 Subject: Add tests for QCollatorSortKey There weren't any, at all. Testing on the CI showed that the implementation is broken on macOS, and, to a lesser extent, on Windows, so blacklist the failing tests until the implementation can be fixed. No need to hold back testing the other implementations. Task-number: QTBUG-58737 Change-Id: I9ae16ab778dbe2e95a6ca5e0bae00df4bad65cb2 Reviewed-by: Edward Welbourne --- tests/auto/corelib/tools/qcollator/BLACKLIST | 18 ++++++++++++++ .../auto/corelib/tools/qcollator/tst_qcollator.cpp | 28 ++++++++++++++++++++-- 2 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 tests/auto/corelib/tools/qcollator/BLACKLIST diff --git a/tests/auto/corelib/tools/qcollator/BLACKLIST b/tests/auto/corelib/tools/qcollator/BLACKLIST new file mode 100644 index 0000000000..09b69e0108 --- /dev/null +++ b/tests/auto/corelib/tools/qcollator/BLACKLIST @@ -0,0 +1,18 @@ +[compare:swedish5] +osx +[compare:norwegian4] +osx +[compare:german6] +osx +windows +[compare:german7] +osx +windows +[compare:german8] +osx +[compare:german9] +osx +[compare:german10] +osx +[compare:french5] +osx diff --git a/tests/auto/corelib/tools/qcollator/tst_qcollator.cpp b/tests/auto/corelib/tools/qcollator/tst_qcollator.cpp index d09910fd5c..ead0992f92 100644 --- a/tests/auto/corelib/tools/qcollator/tst_qcollator.cpp +++ b/tests/auto/corelib/tools/qcollator/tst_qcollator.cpp @@ -33,6 +33,11 @@ #include +Q_CONSTEXPR inline int sign(int i) Q_DECL_NOTHROW +{ return i < 0 ? -1 : i > 0 ? 1 : 0; } + +#define QCOMPARE_SIGN(x, y) QCOMPARE(sign(x), sign(y)) + class tst_QCollator : public QObject { Q_OBJECT @@ -173,9 +178,28 @@ void tst_QCollator::compare() if (numericMode) collator.setNumericMode(true); - QCOMPARE(collator.compare(s1, s2), result); + QCOMPARE_SIGN(collator.compare(s1, s2), result); + { + const auto s1sk = collator.sortKey(s1); + const auto s2sk = collator.sortKey(s2); + + QCOMPARE_SIGN(s1sk.compare(s2sk), result); +#define CHECK(op) QCOMPARE(s1sk op s2sk, result op 0) + CHECK(<); +#undef CHECK + } + collator.setCaseSensitivity(Qt::CaseInsensitive); - QCOMPARE(collator.compare(s1, s2), caseInsensitiveResult); + QCOMPARE_SIGN(collator.compare(s1, s2), caseInsensitiveResult); + { + const auto s1sk = collator.sortKey(s1); + const auto s2sk = collator.sortKey(s2); + + QCOMPARE_SIGN(s1sk.compare(s2sk), caseInsensitiveResult); +#define CHECK(op) QCOMPARE(s1sk op s2sk, caseInsensitiveResult op 0) + CHECK(<); +#undef CHECK + } } -- cgit v1.2.3 From b1b53b7101b800aa7f2b749ffcead8a124c0a5f1 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sun, 12 Feb 2017 08:15:20 +0000 Subject: Revert "Add tests for QCollatorSortKey" This reverts commit b0c1e07d648caf585d2be12cf3d18eb42b86f721. The unit tests it introduced trigger errors with the macOS and Win32 implementations, which means the there's something seriously wrong with either the API, our implementation or the tests. Revert for now until this gets fixed. Blacklisting was also the wrong tool to use. The tests should have used QEXPECT_FAIL. Change-Id: Ida20c6bbe0c019835a22464535029585e8e1e367 Reviewed-by: Thiago Macieira --- tests/auto/corelib/tools/qcollator/BLACKLIST | 18 -------------- .../auto/corelib/tools/qcollator/tst_qcollator.cpp | 28 ++-------------------- 2 files changed, 2 insertions(+), 44 deletions(-) delete mode 100644 tests/auto/corelib/tools/qcollator/BLACKLIST diff --git a/tests/auto/corelib/tools/qcollator/BLACKLIST b/tests/auto/corelib/tools/qcollator/BLACKLIST deleted file mode 100644 index 09b69e0108..0000000000 --- a/tests/auto/corelib/tools/qcollator/BLACKLIST +++ /dev/null @@ -1,18 +0,0 @@ -[compare:swedish5] -osx -[compare:norwegian4] -osx -[compare:german6] -osx -windows -[compare:german7] -osx -windows -[compare:german8] -osx -[compare:german9] -osx -[compare:german10] -osx -[compare:french5] -osx diff --git a/tests/auto/corelib/tools/qcollator/tst_qcollator.cpp b/tests/auto/corelib/tools/qcollator/tst_qcollator.cpp index ead0992f92..d09910fd5c 100644 --- a/tests/auto/corelib/tools/qcollator/tst_qcollator.cpp +++ b/tests/auto/corelib/tools/qcollator/tst_qcollator.cpp @@ -33,11 +33,6 @@ #include -Q_CONSTEXPR inline int sign(int i) Q_DECL_NOTHROW -{ return i < 0 ? -1 : i > 0 ? 1 : 0; } - -#define QCOMPARE_SIGN(x, y) QCOMPARE(sign(x), sign(y)) - class tst_QCollator : public QObject { Q_OBJECT @@ -178,28 +173,9 @@ void tst_QCollator::compare() if (numericMode) collator.setNumericMode(true); - QCOMPARE_SIGN(collator.compare(s1, s2), result); - { - const auto s1sk = collator.sortKey(s1); - const auto s2sk = collator.sortKey(s2); - - QCOMPARE_SIGN(s1sk.compare(s2sk), result); -#define CHECK(op) QCOMPARE(s1sk op s2sk, result op 0) - CHECK(<); -#undef CHECK - } - + QCOMPARE(collator.compare(s1, s2), result); collator.setCaseSensitivity(Qt::CaseInsensitive); - QCOMPARE_SIGN(collator.compare(s1, s2), caseInsensitiveResult); - { - const auto s1sk = collator.sortKey(s1); - const auto s2sk = collator.sortKey(s2); - - QCOMPARE_SIGN(s1sk.compare(s2sk), caseInsensitiveResult); -#define CHECK(op) QCOMPARE(s1sk op s2sk, caseInsensitiveResult op 0) - CHECK(<); -#undef CHECK - } + QCOMPARE(collator.compare(s1, s2), caseInsensitiveResult); } -- cgit v1.2.3 From 418184c2a0ad97cce12717a43f84fa6f12ece189 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 27 Jan 2017 11:26:42 -0800 Subject: Update the __xxx__ macros due to MSVC and ICC not defining them all And if __AES__ or __PCLMUL__ is defined, then we expect wmmintrin.h to exist. Change-Id: I445bb15619f6401494e8fffd149db77dc513e071 Reviewed-by: Allan Sandfeld Jensen --- src/corelib/tools/qsimd_p.h | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/corelib/tools/qsimd_p.h b/src/corelib/tools/qsimd_p.h index be53c51c48..f6164e2297 100644 --- a/src/corelib/tools/qsimd_p.h +++ b/src/corelib/tools/qsimd_p.h @@ -230,12 +230,39 @@ // SSE4.2 intrinsics #if defined(__SSE4_2__) || (defined(QT_COMPILER_SUPPORTS_SSE4_2) && defined(QT_COMPILER_SUPPORTS_SIMD_ALWAYS)) #include + +# if defined(__SSE4_2__) && defined(QT_COMPILER_SUPPORTS_SIMD_ALWAYS) +// POPCNT instructions: +// All processors that support SSE4.2 support POPCNT +// (but neither MSVC nor the Intel compiler define this macro) +# define __POPCNT__ 1 +# endif #endif // AVX intrinsics #if defined(__AVX__) || (defined(QT_COMPILER_SUPPORTS_AVX) && defined(QT_COMPILER_SUPPORTS_SIMD_ALWAYS)) // immintrin.h is the ultimate header, we don't need anything else after this #include + +# if defined(__AVX__) && defined(QT_COMPILER_SUPPORTS_SIMD_ALWAYS) +// AES, PCLMULQDQ instructions: +// All processors that support AVX support AES, PCLMULQDQ +// (but neither MSVC nor the Intel compiler define these macros) +# define __AES__ 1 +# define __PCLMUL__ 1 +# endif + +# if defined(__AVX2__) && defined(QT_COMPILER_SUPPORTS_SIMD_ALWAYS) +// F16C & RDRAND instructions: +// All processors that support AVX2 support F16C & RDRAND: +// (but neither MSVC nor the Intel compiler define these macros) +# define __F16C__ 1 +# define __RDRND__ 1 +# endif +#endif + +#if defined(__AES__) || defined(__PCLMUL__) +# include #endif #define QT_FUNCTION_TARGET_STRING_SSE2 "sse2" @@ -255,7 +282,10 @@ #define QT_FUNCTION_TARGET_STRING_AVX512IFMA "avx512ifma" #define QT_FUNCTION_TARGET_STRING_AVX512VBMI "avx512vbmi" -#define QT_FUNCTION_TARGET_STRING_F16C "f16c" +#define QT_FUNCTION_TARGET_STRING_AES "aes,sse4.2" +#define QT_FUNCTION_TARGET_STRING_PCLMUL "pclmul,sse4.2" +#define QT_FUNCTION_TARGET_STRING_POPCNT "popcnt" +#define QT_FUNCTION_TARGET_STRING_F16C "f16c,avx" #define QT_FUNCTION_TARGET_STRING_RDRAND "rdrnd" #define QT_FUNCTION_TARGET_STRING_BMI "bmi" #define QT_FUNCTION_TARGET_STRING_BMI2 "bmi2" -- cgit v1.2.3 From b2ffc4d0a0c7e498da68851b97963a8b7534847d Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 10 Feb 2017 20:44:11 -0800 Subject: moc: remember to quote the path to moc_predefs.h Task-number: QTBUG-58764 Change-Id: I4baef2edf7624ad69d96fffd14a22209a2bb90be Reviewed-by: Olivier Goffart (Woboq GmbH) --- mkspecs/features/moc.prf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkspecs/features/moc.prf b/mkspecs/features/moc.prf index 825c706950..cded960de1 100644 --- a/mkspecs/features/moc.prf +++ b/mkspecs/features/moc.prf @@ -57,7 +57,7 @@ defineReplace(mocCmdBase) { msvc: RET += --compiler-flavor=msvc isEmpty(MOC_PREDEF_FILE): RET += $$join(QMAKE_COMPILER_DEFINES, " -D", -D) - else: RET += --include $$moc_predefs.output + else: RET += --include $$shell_quote($$moc_predefs.output) RET += $$incvar $$QMAKE_MOC_OPTIONS return($$RET) -- cgit v1.2.3 From 9db8c171e7bd775ed4cf5281fe17f8084c0ffbcd Mon Sep 17 00:00:00 2001 From: Filipe Azevedo Date: Thu, 23 Jun 2016 15:01:40 +0200 Subject: Fix QLibrary::isLibrary on Apple platforms Add proper support for 'so' and 'bundle' suffixes. Qt wrongly assumes .so libraries are not versioned on Apple platforms, which is wrong. Also, the shared library .bundle which is what Apple recommends instead of .so, are also versioned (not to be confound with the different Core Foundation bundles, which are directory hierarchy). For more info, see http://docstore.mik.ua/orelly/unix3/mac/ch05_03.htm. Especially the part that reads: "Loadable modules, called bundles in Mac OS X, have the file type MH_BUNDLE. Most Unix-based software ports usually produce bundles with a .so extension, for the sake of consistency across platforms. Although Apple recommends giving bundles a .bundle extension, it isn't mandatory." Task-number: QTBUG-50446 Change-Id: Iacd5136397a12d65d83821434f332eb602550b4b Reviewed-by: Thiago Macieira --- src/corelib/plugin/qlibrary.cpp | 34 +++++++++------------- .../auto/corelib/plugin/qlibrary/tst_qlibrary.cpp | 9 ++---- 2 files changed, 15 insertions(+), 28 deletions(-) diff --git a/src/corelib/plugin/qlibrary.cpp b/src/corelib/plugin/qlibrary.cpp index aff2991ed1..96cf5371f9 100644 --- a/src/corelib/plugin/qlibrary.cpp +++ b/src/corelib/plugin/qlibrary.cpp @@ -617,40 +617,34 @@ bool QLibrary::isLibrary(const QString &fileName) { #if defined(Q_OS_WIN) return fileName.endsWith(QLatin1String(".dll"), Qt::CaseInsensitive); -#else +#else // Generic Unix QString completeSuffix = QFileInfo(fileName).completeSuffix(); if (completeSuffix.isEmpty()) return false; const QVector suffixes = completeSuffix.splitRef(QLatin1Char('.')); -# if defined(Q_OS_DARWIN) - - // On Mac, libs look like libmylib.1.0.0.dylib - const QStringRef &lastSuffix = suffixes.at(suffixes.count() - 1); - const QStringRef &firstSuffix = suffixes.at(0); - - bool valid = (lastSuffix == QLatin1String("dylib") - || firstSuffix == QLatin1String("so") - || firstSuffix == QLatin1String("bundle")); - - return valid; -# else // Generic Unix QStringList validSuffixList; -# if defined(Q_OS_HPUX) +# if defined(Q_OS_HPUX) /* See "HP-UX Linker and Libraries User's Guide", section "Link-time Differences between PA-RISC and IPF": "In PA-RISC (PA-32 and PA-64) shared libraries are suffixed with .sl. In IPF (32-bit and 64-bit), the shared libraries are suffixed with .so. For compatibility, the IPF linker also supports the .sl suffix." */ validSuffixList << QLatin1String("sl"); -# if defined __ia64 +# if defined __ia64 validSuffixList << QLatin1String("so"); -# endif -# elif defined(Q_OS_AIX) +# endif +# elif defined(Q_OS_AIX) validSuffixList << QLatin1String("a") << QLatin1String("so"); -# elif defined(Q_OS_UNIX) +# elif defined(Q_OS_DARWIN) + // On Apple platforms, dylib look like libmylib.1.0.0.dylib + if (suffixes.last() == QLatin1String("dylib")) + return true; + + validSuffixList << QLatin1String("so") << QLatin1String("bundle"); +# elif defined(Q_OS_UNIX) validSuffixList << QLatin1String("so"); -# endif +# endif // Examples of valid library names: // libfoo.so @@ -669,9 +663,7 @@ bool QLibrary::isLibrary(const QString &fileName) if (i != suffixPos) suffixes.at(i).toInt(&valid); return valid; -# endif #endif - } typedef const char * (*QtPluginQueryVerificationDataFunction)(); diff --git a/tests/auto/corelib/plugin/qlibrary/tst_qlibrary.cpp b/tests/auto/corelib/plugin/qlibrary/tst_qlibrary.cpp index 42fed1191b..16177bb0b7 100644 --- a/tests/auto/corelib/plugin/qlibrary/tst_qlibrary.cpp +++ b/tests/auto/corelib/plugin/qlibrary/tst_qlibrary.cpp @@ -302,15 +302,10 @@ void tst_QLibrary::isLibrary_data() QTest::newRow(".sl") << QString("mylib.sl") << sl_VALID; QTest::newRow(".so") << QString("mylib.so") << so_VALID; QTest::newRow(".so+version") << QString("mylib.so.0") << so_VALID; - - // special tests: -#ifndef Q_OS_MAC QTest::newRow("version+.so") << QString("libc-2.7.so") << so_VALID; QTest::newRow("version+.so+version") << QString("liboil-0.3.so.0.1.0") << so_VALID; -#else - QTest::newRow("version+.so") << QString("libc-2.7.so") << false; - QTest::newRow("version+.so+version") << QString("liboil-0.3.so.0.1.0") << false; -#endif + + // special tests: #ifdef Q_OS_MAC QTest::newRow("good (libmylib.1.0.0.dylib)") << QString("libmylib.1.0.0.dylib") << true; QTest::newRow("good (libmylib.dylib)") << QString("libmylib.dylib") << true; -- cgit v1.2.3 From 088bf1fb3da72d5c153e5ce16431cc6d96159eae Mon Sep 17 00:00:00 2001 From: Dominik Holland Date: Tue, 7 Feb 2017 13:56:02 +0100 Subject: Add the new NVIDIA specific calls to qeglstreamconvenience NVIDIA 370 introduced new calls needed to work with eglstreams in the wayland window system Task-number: QTBUG-58299 Change-Id: I606b143d3016f365b0d5ca4bc163b99289afbea1 Reviewed-by: Laszlo Agocs --- .../eglconvenience/qeglstreamconvenience.cpp | 5 +++++ .../eglconvenience/qeglstreamconvenience_p.h | 13 +++++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/platformsupport/eglconvenience/qeglstreamconvenience.cpp b/src/platformsupport/eglconvenience/qeglstreamconvenience.cpp index 1e90f849f5..5c336f0553 100644 --- a/src/platformsupport/eglconvenience/qeglstreamconvenience.cpp +++ b/src/platformsupport/eglconvenience/qeglstreamconvenience.cpp @@ -103,6 +103,11 @@ void QEGLStreamConvenience::initialize(EGLDisplay dpy) stream_consumer_gltexture = reinterpret_cast(eglGetProcAddress("eglStreamConsumerGLTextureExternalKHR")); stream_consumer_acquire = reinterpret_cast(eglGetProcAddress("eglStreamConsumerAcquireKHR")); stream_consumer_release = reinterpret_cast(eglGetProcAddress("eglStreamConsumerReleaseKHR")); + create_stream_attrib_nv = reinterpret_cast(eglGetProcAddress("eglCreateStreamAttribNV")); + set_stream_attrib_nv = reinterpret_cast(eglGetProcAddress("eglSetStreamAttribNV")); + query_stream_attrib_nv = reinterpret_cast(eglGetProcAddress("eglQueryStreamAttribNV")); + acquire_stream_attrib_nv = reinterpret_cast(eglGetProcAddress("eglStreamConsumerAcquireAttribNV")); + release_stream_attrib_nv = reinterpret_cast(eglGetProcAddress("eglStreamConsumerReleaseAttribNV")); has_egl_stream = strstr(extensions, "EGL_KHR_stream"); has_egl_stream_producer_eglsurface = strstr(extensions, "EGL_KHR_stream_producer_eglsurface"); diff --git a/src/platformsupport/eglconvenience/qeglstreamconvenience_p.h b/src/platformsupport/eglconvenience/qeglstreamconvenience_p.h index 8c802a0ca1..c3d3070210 100644 --- a/src/platformsupport/eglconvenience/qeglstreamconvenience_p.h +++ b/src/platformsupport/eglconvenience/qeglstreamconvenience_p.h @@ -148,6 +148,14 @@ typedef EGLBoolean (EGLAPIENTRYP PFNEGLSTREAMCONSUMEROUTPUTEXTPROC) (EGLDisplay #define EGL_PLATFORM_X11_KHR 0x31D5 #endif +#ifndef EGL_NV_stream_attrib +typedef EGLStreamKHR (EGLAPIENTRYP PFNEGLCREATESTREAMATTRIBNVPROC) (EGLDisplay dpy, const EGLAttrib *attrib_list); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLSETSTREAMATTRIBNVPROC) (EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, EGLAttrib value); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYSTREAMATTRIBNVPROC) (EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, EGLAttrib *value); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLSTREAMCONSUMERACQUIREATTRIBNVPROC) (EGLDisplay dpy, EGLStreamKHR stream, const EGLAttrib *attrib_list); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLSTREAMCONSUMERRELEASEATTRIBNVPROC) (EGLDisplay dpy, EGLStreamKHR stream, const EGLAttrib *attrib_list); +#endif + QT_BEGIN_NAMESPACE class QEGLStreamConvenience @@ -160,6 +168,11 @@ public: PFNEGLQUERYDEVICESEXTPROC query_devices; PFNEGLQUERYDEVICESTRINGEXTPROC query_device_string; PFNEGLCREATESTREAMKHRPROC create_stream; + PFNEGLCREATESTREAMATTRIBNVPROC create_stream_attrib_nv; + PFNEGLSETSTREAMATTRIBNVPROC set_stream_attrib_nv; + PFNEGLQUERYSTREAMATTRIBNVPROC query_stream_attrib_nv; + PFNEGLSTREAMCONSUMERACQUIREATTRIBNVPROC acquire_stream_attrib_nv; + PFNEGLSTREAMCONSUMERRELEASEATTRIBNVPROC release_stream_attrib_nv; PFNEGLDESTROYSTREAMKHRPROC destroy_stream; PFNEGLSTREAMATTRIBKHRPROC stream_attrib; PFNEGLQUERYSTREAMKHRPROC query_stream; -- cgit v1.2.3 From e3e2bc21097ee20f038c15f699e829c292251c18 Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Mon, 13 Feb 2017 09:57:02 +0100 Subject: announce 'temporaryfile' feature in bootstrap library qmldevtools refers to it. Task-number: QTBUG-58819 Change-Id: Id88265bb17e4d2e9c61f77409c4163eacc4a13f3 Reviewed-by: Lars Knoll --- src/corelib/global/qconfig-bootstrapped.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/corelib/global/qconfig-bootstrapped.h b/src/corelib/global/qconfig-bootstrapped.h index d7849d4699..1c806e0774 100644 --- a/src/corelib/global/qconfig-bootstrapped.h +++ b/src/corelib/global/qconfig-bootstrapped.h @@ -77,6 +77,7 @@ #define QT_NO_SYSTEMLOCALE #define QT_FEATURE_slog2 -1 #define QT_FEATURE_syslog -1 +#define QT_FEATURE_temporaryfile 1 #define QT_NO_THREAD #define QT_FEATURE_timezone -1 #define QT_FEATURE_topleveldomain -1 -- cgit v1.2.3 From e8a2a9bf307de0bd174e438c87045ca1b846d73e Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Thu, 9 Feb 2017 08:40:50 +0100 Subject: Windows QPA: Include GL header depending on presence of dynamic GL Amends change 7780ee9e5f20f80ab9e053058d0b6d92586cf876. Task-number: QTBUG-58178 Change-Id: I0b6e064dfdbdafb7fba9c20c56cfd873fa594c44 Reviewed-by: Joerg Bornemann --- src/plugins/platforms/windows/qwindowswindow.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index b5522c1f90..48835f26a6 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -43,7 +43,11 @@ #include "qwindowsscreen.h" #include "qwindowsintegration.h" #include "qwindowsnativeinterface.h" -#include "qwindowsglcontext.h" +#if QT_CONFIG(dynamicgl) +# include "qwindowsglcontext.h" +#else +# include "qwindowsopenglcontext.h" +#endif #ifdef QT_NO_CURSOR # include "qwindowscursor.h" #endif -- cgit v1.2.3 From 6fe386ac426a7ac41be57e15b02041ff809c8eac Mon Sep 17 00:00:00 2001 From: Nico Vertriest Date: Tue, 17 Jan 2017 16:40:56 +0100 Subject: Doc: Updated doc QSqlDatabase Change-Id: I914e9bdbf6137f0e3858a57b0f59fc550fc7e317 Reviewed-by: Venugopal Shivashankar --- src/sql/kernel/qsqldatabase.cpp | 79 ++++++++++++++++++++++++++--------------- 1 file changed, 51 insertions(+), 28 deletions(-) diff --git a/src/sql/kernel/qsqldatabase.cpp b/src/sql/kernel/qsqldatabase.cpp index d91e502b96..0416215870 100644 --- a/src/sql/kernel/qsqldatabase.cpp +++ b/src/sql/kernel/qsqldatabase.cpp @@ -313,7 +313,7 @@ void QSqlDatabasePrivate::disable() /*! \class QSqlDatabase - \brief The QSqlDatabase class represents a connection to + \brief The QSqlDatabase class handles a connection to a database. \ingroup database @@ -332,18 +332,17 @@ void QSqlDatabasePrivate::disable() Create a connection (i.e., an instance of QSqlDatabase) by calling one of the static addDatabase() functions, where you specify \l{SQL Database Drivers#Supported Databases} {the driver or type - of driver} to use (i.e., what kind of database will you access?) + of driver} to use (depending on the type of database) and a connection name. A connection is known by its own name, \e{not} by the name of the database it connects to. You can have multiple connections to one database. QSqlDatabase also supports the concept of a \e{default} connection, which is the unnamed connection. To create the default connection, don't pass the connection name argument when you call addDatabase(). - Subsequently, when you call any static member function that takes - the connection name argument, if you don't pass the connection - name argument, the default connection is assumed. The following - snippet shows how to create and open a default connection to a - PostgreSQL database: + Subsequently, the default connection will be assumed if you call + any static member function without specifying the connection name. + The following snippet shows how to create and open a default connection + to a PostgreSQL database: \snippet sqldatabase/sqldatabase.cpp 0 @@ -374,27 +373,50 @@ void QSqlDatabasePrivate::disable() referenced by other QSqlDatabase objects. Use contains() to see if a given connection name is in the list of connections. - Once a connection is established, you can call tables() to get the - list of tables in the database, call primaryIndex() to get a - table's primary index, and call record() to get meta-information - about a table's fields (e.g., field names). + \table + \header + \li {2,1}Some utility methods: + \row + \li tables() + \li returns the list of tables + \row + \li primaryIndex() + \li returns a table's primary index + \row + \li record() + \li returns meta-information about a table's fields + \row + \li transaction() + \li starts a transaction + \row + \li commit() + \li saves and completes a transaction + \row + \li rollback() + \li cancels a transaction + \row + \li hasFeature() + \li checks if a driver supports transactions + \row + \li lastError() + \li returns information about the last error + \row + \li drivers() + \li returns the names of the available SQL drivers + \row + \li isDriverAvailable() + \li checks if a particular driver is available + \row + \li registerSqlDriver() + \li registers a custom-made driver + \endtable \note QSqlDatabase::exec() is deprecated. Use QSqlQuery::exec() instead. - If the driver supports transactions, use transaction() to start a - transaction, and commit() or rollback() to complete it. Use - \l{QSqlDriver::} {hasFeature()} to ask if the driver supports - transactions. \note When using transactions, you must start the + \note When using transactions, you must start the transaction before you create your query. - If an error occurs, lastError() will return information about it. - - Get the names of the available SQL drivers with drivers(). Check - for the presence of a particular driver with isDriverAvailable(). - If you have created your own custom driver, you must register it - with registerSqlDriver(). - \sa QSqlDriver, QSqlQuery, {Qt SQL}, {Threads and the SQL Module} */ @@ -963,9 +985,9 @@ QString QSqlDatabase::userName() const } /*! - Returns the connection's password. If the password was not set - with setPassword(), and if the password was given in the open() - call, or if no password was used, an empty string is returned. + Returns the connection's password. An empty string will be returned + if the password was not set with setPassword(), and if the password + was given in the open() call, or if no password was used. */ QString QSqlDatabase::password() const { @@ -1045,7 +1067,7 @@ QStringList QSqlDatabase::tables(QSql::TableType type) const /*! Returns the primary index for table \a tablename. If no primary - index exists an empty QSqlIndex is returned. + index exists, an empty QSqlIndex is returned. \sa tables(), record() */ @@ -1071,8 +1093,9 @@ QSqlRecord QSqlDatabase::record(const QString& tablename) const /*! Sets database-specific \a options. This must be done before the - connection is opened or it has no effect (or you can close() the - connection, call this function and open() the connection again). + connection is opened, otherwise it has no effect. Another possibility + is to close the connection, call QSqlDatabase::setConnectOptions(), + and open() the connection again. The format of the \a options string is a semicolon separated list of option names or option=value pairs. The options depend on the -- cgit v1.2.3 From aa1631d76c37793941b7dc343141962ccf0488be Mon Sep 17 00:00:00 2001 From: Julien Gueytat Date: Sat, 11 Feb 2017 18:48:50 +0100 Subject: Replace gstrip by strip due to ELF corruption This is an old error of the gstrip binutils. The bug has been corrected and re-introduced. The command *elfdump -d xxx* on the ELF does bring lines like those : .SUNW_syminfo: invalid sh_info: 0 Task-number: QTBUG-58814 Change-Id: I330c4031dcf4ba64297df4b333b41cf0a003914f Reviewed-by: Oswald Buddenhagen --- mkspecs/common/solaris.conf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mkspecs/common/solaris.conf b/mkspecs/common/solaris.conf index b53227204d..23e26debff 100644 --- a/mkspecs/common/solaris.conf +++ b/mkspecs/common/solaris.conf @@ -29,5 +29,5 @@ QMAKE_OBJCOPY = objcopy QMAKE_NM = nm -P QMAKE_RANLIB = -QMAKE_STRIP = gstrip -QMAKE_STRIPFLAGS_LIB += --strip-unneeded +QMAKE_STRIP = strip +QMAKE_STRIPFLAGS_LIB += -- cgit v1.2.3 From 10ecbc4041cb7db004f4ed4d40ce082553d75844 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Wed, 8 Feb 2017 12:04:54 +0100 Subject: Blacklist tst_QWaitCondition::wakeOne() on Windows This test was determined to be flaky on the CI. Task-number: QTBUG-58741 Change-Id: I43196d3a27f726fb96b427f5071e726b571a0404 Reviewed-by: Friedemann Kleint Reviewed-by: David Faure --- tests/auto/corelib/thread/qwaitcondition/BLACKLIST | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 tests/auto/corelib/thread/qwaitcondition/BLACKLIST diff --git a/tests/auto/corelib/thread/qwaitcondition/BLACKLIST b/tests/auto/corelib/thread/qwaitcondition/BLACKLIST new file mode 100644 index 0000000000..3ff336576b --- /dev/null +++ b/tests/auto/corelib/thread/qwaitcondition/BLACKLIST @@ -0,0 +1,2 @@ +[wakeOne] +windows -- cgit v1.2.3