diff options
author | Liang Qi <liang.qi@qt.io> | 2016-05-12 07:31:50 +0200 |
---|---|---|
committer | Liang Qi <liang.qi@qt.io> | 2016-05-12 08:33:08 +0200 |
commit | 990969655c5fb4d03682e96df9b12101f5ee9815 (patch) | |
tree | b8fb5c50285105c8bc5a938fb50f93ff9f24889d /src/gui | |
parent | a213011a53f12f101d08a04afc8fdacd2d54a232 (diff) | |
parent | e64b2234e829cc47872225debcf80d6c06db18f0 (diff) |
Merge remote-tracking branch 'origin/5.7' into dev
Conflicts:
config_help.txt
configure
src/corelib/io/qprocess_wince.cpp
src/plugins/platforms/windows/qwindowstheme.cpp
src/plugins/platforms/xcb/qxcbbackingstore.cpp
tests/auto/corelib/tools/qtimezone/BLACKLIST
tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp
tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp
Change-Id: I26644d1cb3b78412c8ff285e2a55bea1bd641c01
Diffstat (limited to 'src/gui')
33 files changed, 490 insertions, 205 deletions
diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp index 162a1a20b8..3238d83729 100644 --- a/src/gui/image/qimage.cpp +++ b/src/gui/image/qimage.cpp @@ -2454,10 +2454,16 @@ QColor QImage::pixelColor(int x, int y) const */ void QImage::setPixelColor(int x, int y, const QColor &color) { - if (!d || x < 0 || x >= width() || y < 0 || y >= height() || !color.isValid()) { + if (!d || x < 0 || x >= width() || y < 0 || y >= height()) { qWarning("QImage::setPixelColor: coordinate (%d,%d) out of range", x, y); return; } + + if (!color.isValid()) { + qWarning("QImage::setPixelColor: color is invalid"); + return; + } + // QColor is always unpremultiplied QRgba64 c = color.rgba64(); if (!hasAlphaChannel()) diff --git a/src/gui/image/qimage_conversions.cpp b/src/gui/image/qimage_conversions.cpp index 41b4807671..16789c802e 100644 --- a/src/gui/image/qimage_conversions.cpp +++ b/src/gui/image/qimage_conversions.cpp @@ -1747,24 +1747,30 @@ static void convert_Indexed8_to_X32(QImageData *dest, const QImageData *src, Qt: Q_ASSERT(src->width == dest->width); Q_ASSERT(src->height == dest->height); - QVector<QRgb> colorTable = fix_color_table(src->colortable, dest->format); + QVector<QRgb> colorTable = src->has_alpha_clut ? fix_color_table(src->colortable, dest->format) : src->colortable; if (colorTable.size() == 0) { colorTable.resize(256); for (int i=0; i<256; ++i) colorTable[i] = qRgb(i, i, i); } + if (colorTable.size() < 256) { + int tableSize = colorTable.size(); + colorTable.resize(256); + for (int i=tableSize; i<256; ++i) + colorTable[i] = 0; + } int w = src->width; const uchar *src_data = src->data; uchar *dest_data = dest->data; - int tableSize = colorTable.size() - 1; + const QRgb *colorTablePtr = colorTable.constData(); for (int y = 0; y < src->height; y++) { - uint *p = (uint *)dest_data; + uint *p = reinterpret_cast<uint *>(dest_data); const uchar *b = src_data; uint *end = p + w; while (p < end) - *p++ = colorTable.at(qMin<int>(tableSize, *b++)); + *p++ = colorTablePtr[*b++]; src_data += src->bytes_per_line; dest_data += dest->bytes_per_line; diff --git a/src/gui/image/qimagewriter.cpp b/src/gui/image/qimagewriter.cpp index 1a0bb4d6ff..f3af2738af 100644 --- a/src/gui/image/qimagewriter.cpp +++ b/src/gui/image/qimagewriter.cpp @@ -837,9 +837,9 @@ void supportedImageHandlerMimeTypes(QFactoryLoader *loader, const int pluginCount = metaDataList.size(); for (int i = 0; i < pluginCount; ++i) { - const QJsonObject metaData = metaDataList.at(i).value(QStringLiteral("MetaData")).toObject(); - const QJsonArray keys = metaData.value(QStringLiteral("Keys")).toArray(); - const QJsonArray mimeTypes = metaData.value(QStringLiteral("MimeTypes")).toArray(); + const QJsonObject metaData = metaDataList.at(i).value(QLatin1String("MetaData")).toObject(); + const QJsonArray keys = metaData.value(QLatin1String("Keys")).toArray(); + const QJsonArray mimeTypes = metaData.value(QLatin1String("MimeTypes")).toArray(); QImageIOPlugin *plugin = qobject_cast<QImageIOPlugin *>(loader->instance(i)); const int keyCount = keys.size(); for (int k = 0; k < keyCount; ++k) { diff --git a/src/gui/kernel/qinputmethod.cpp b/src/gui/kernel/qinputmethod.cpp index b81e166d3a..365b088840 100644 --- a/src/gui/kernel/qinputmethod.cpp +++ b/src/gui/kernel/qinputmethod.cpp @@ -183,6 +183,18 @@ QRectF QInputMethod::keyboardRectangle() const } /*! + \property QInputMethod::inputItemClipRectangle + \brief Input item's clipped rectangle in window coordinates. + + The clipped input rectangle is often used by various input methods to determine + how much screen real estate is available for the input method (e.g. Virtual Keyboard). +*/ +QRectF QInputMethod::inputItemClipRectangle() const +{ + Q_D(const QInputMethod); + return inputMethodQueryRectangle_helper(Qt::ImInputItemClipRectangle, d->inputItemTransform); +} +/*! Requests virtual keyboard to open. If the platform doesn't provide virtual keyboard the visibility remains false. @@ -320,6 +332,8 @@ void QInputMethod::update(Qt::InputMethodQueries queries) if (queries & (Qt::ImAnchorRectangle)) emit anchorRectangleChanged(); + if (queries & (Qt::ImInputItemClipRectangle)) + emit inputItemClipRectangleChanged(); } /*! diff --git a/src/gui/kernel/qinputmethod.h b/src/gui/kernel/qinputmethod.h index 22e4677eaa..1a63d1314e 100644 --- a/src/gui/kernel/qinputmethod.h +++ b/src/gui/kernel/qinputmethod.h @@ -57,6 +57,7 @@ class Q_GUI_EXPORT QInputMethod : public QObject Q_PROPERTY(QRectF cursorRectangle READ cursorRectangle NOTIFY cursorRectangleChanged) Q_PROPERTY(QRectF anchorRectangle READ anchorRectangle NOTIFY anchorRectangleChanged) Q_PROPERTY(QRectF keyboardRectangle READ keyboardRectangle NOTIFY keyboardRectangleChanged) + Q_PROPERTY(QRectF inputItemClipRectangle READ inputItemClipRectangle NOTIFY inputItemClipRectangleChanged) Q_PROPERTY(bool visible READ isVisible NOTIFY visibleChanged) Q_PROPERTY(bool animating READ isAnimating NOTIFY animatingChanged) Q_PROPERTY(QLocale locale READ locale NOTIFY localeChanged) @@ -76,6 +77,8 @@ public: // keyboard geometry in window coords QRectF keyboardRectangle() const; + QRectF inputItemClipRectangle() const; + enum Action { Click, ContextMenu @@ -106,6 +109,7 @@ Q_SIGNALS: void cursorRectangleChanged(); void anchorRectangleChanged(); void keyboardRectangleChanged(); + void inputItemClipRectangleChanged(); void visibleChanged(); void animatingChanged(); void localeChanged(); diff --git a/src/gui/kernel/qkeysequence.cpp b/src/gui/kernel/qkeysequence.cpp index 8a6eeb4cc9..8ccd85795b 100644 --- a/src/gui/kernel/qkeysequence.cpp +++ b/src/gui/kernel/qkeysequence.cpp @@ -1643,7 +1643,7 @@ QDataStream &operator>>(QDataStream &s, QKeySequence &keysequence) s >> keys[i]; } qAtomicDetach(keysequence.d); - std::copy(keys, keys + MaxKeys, keysequence.d->key); + std::copy(keys, keys + MaxKeys, QT_MAKE_CHECKED_ARRAY_ITERATOR(keysequence.d->key, MaxKeys)); return s; } diff --git a/src/gui/kernel/qkeysequence_p.h b/src/gui/kernel/qkeysequence_p.h index eeea0f5772..116e91c0cd 100644 --- a/src/gui/kernel/qkeysequence_p.h +++ b/src/gui/kernel/qkeysequence_p.h @@ -76,7 +76,8 @@ public: } inline QKeySequencePrivate(const QKeySequencePrivate ©) : ref(1) { - std::copy(copy.key, copy.key + MaxKeyCount, key); + std::copy(copy.key, copy.key + MaxKeyCount, + QT_MAKE_CHECKED_ARRAY_ITERATOR(key, MaxKeyCount)); } QAtomicInt ref; int key[MaxKeyCount]; diff --git a/src/gui/kernel/qwindow.cpp b/src/gui/kernel/qwindow.cpp index 107ea73a56..81310ae2a2 100644 --- a/src/gui/kernel/qwindow.cpp +++ b/src/gui/kernel/qwindow.cpp @@ -2146,6 +2146,17 @@ bool QWindow::event(QEvent *ev) break; } + case QEvent::PlatformSurface: { + if ((static_cast<QPlatformSurfaceEvent *>(ev))->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) { +#ifndef QT_NO_OPENGL + QOpenGLContext *context = QOpenGLContext::currentContext(); + if (context && context->surface() == static_cast<QSurface *>(this)) + context->doneCurrent(); +#endif + } + break; + } + default: return QObject::event(ev); } diff --git a/src/gui/opengl/qopengl.h b/src/gui/opengl/qopengl.h index 745adda969..5f9d78b780 100644 --- a/src/gui/opengl/qopengl.h +++ b/src/gui/opengl/qopengl.h @@ -100,6 +100,9 @@ typedef void* GLeglImageOES; # elif defined(QT_OPENGL_ES_3) # include <GLES3/gl3.h> # else +# ifndef GL_GLEXT_PROTOTYPES +# define GL_GLEXT_PROTOTYPES +# endif # include <GLES2/gl2.h> #endif diff --git a/src/gui/opengl/qopenglcustomshaderstage.cpp b/src/gui/opengl/qopenglcustomshaderstage.cpp index a0386bd5f3..baa44f86b0 100644 --- a/src/gui/opengl/qopenglcustomshaderstage.cpp +++ b/src/gui/opengl/qopenglcustomshaderstage.cpp @@ -69,6 +69,7 @@ QOpenGLCustomShaderStage::~QOpenGLCustomShaderStage() d->m_manager->removeCustomStage(); d->m_manager->sharedShaders->cleanupCustomStage(this); } + delete d_ptr; } void QOpenGLCustomShaderStage::setUniformsDirty() diff --git a/src/gui/opengl/qopenglcustomshaderstage_p.h b/src/gui/opengl/qopenglcustomshaderstage_p.h index 099e6aadf8..2342991e5e 100644 --- a/src/gui/opengl/qopenglcustomshaderstage_p.h +++ b/src/gui/opengl/qopenglcustomshaderstage_p.h @@ -52,6 +52,7 @@ // #include <QOpenGLShaderProgram> +#include <QtGlobal> QT_BEGIN_NAMESPACE @@ -78,6 +79,8 @@ protected: private: QOpenGLCustomShaderStagePrivate* d_ptr; + + Q_DISABLE_COPY(QOpenGLCustomShaderStage) }; diff --git a/src/gui/opengl/qopengltextureglyphcache.cpp b/src/gui/opengl/qopengltextureglyphcache.cpp index 8f69095464..9a7b1eb21d 100644 --- a/src/gui/opengl/qopengltextureglyphcache.cpp +++ b/src/gui/opengl/qopengltextureglyphcache.cpp @@ -181,6 +181,94 @@ void QOpenGLTextureGlyphCache::setupVertexAttribs() m_buffer.release(); } +static void load_glyph_image_to_texture(QOpenGLContext *ctx, + QImage &img, + GLuint texture, + int tx, int ty) +{ + QOpenGLFunctions *funcs = ctx->functions(); + + const int imgWidth = img.width(); + const int imgHeight = img.height(); + + if (img.format() == QImage::Format_Mono) { + img = img.convertToFormat(QImage::Format_Grayscale8); + } else if (img.depth() == 32) { + if (img.format() == QImage::Format_RGB32 + // We need to make the alpha component equal to the average of the RGB values. + // This is needed when drawing sub-pixel antialiased text on translucent targets. +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + || img.format() == QImage::Format_ARGB32_Premultiplied +#else + || (img.format() == QImage::Format_ARGB32_Premultiplied + && ctx->isOpenGLES()) +#endif + ) { + for (int y = 0; y < imgHeight; ++y) { + QRgb *src = (QRgb *) img.scanLine(y); + for (int x = 0; x < imgWidth; ++x) { + int r = qRed(src[x]); + int g = qGreen(src[x]); + int b = qBlue(src[x]); + int avg; + if (img.format() == QImage::Format_RGB32) + avg = (r + g + b + 1) / 3; // "+1" for rounding. + else // Format_ARGB_Premultiplied + avg = qAlpha(src[x]); + + src[x] = qRgba(r, g, b, avg); + // swizzle the bits to accommodate for the GL_RGBA upload. +#if Q_BYTE_ORDER != Q_BIG_ENDIAN + if (ctx->isOpenGLES()) +#endif + src[x] = ARGB2RGBA(src[x]); + } + } + } + } + + funcs->glBindTexture(GL_TEXTURE_2D, texture); + if (img.depth() == 32) { +#ifdef QT_OPENGL_ES_2 + GLenum fmt = GL_RGBA; +#else + GLenum fmt = ctx->isOpenGLES() ? GL_RGBA : GL_BGRA; +#endif // QT_OPENGL_ES_2 + +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + fmt = GL_RGBA; +#endif + funcs->glTexSubImage2D(GL_TEXTURE_2D, 0, tx, ty, imgWidth, imgHeight, fmt, GL_UNSIGNED_BYTE, img.constBits()); + } else { + // The scanlines in image are 32-bit aligned, even for mono or 8-bit formats. This + // is good because it matches the default of 4 bytes for GL_UNPACK_ALIGNMENT. +#if !defined(QT_OPENGL_ES_2) + const GLenum format = isCoreProfile() ? GL_RED : GL_ALPHA; +#else + const GLenum format = GL_ALPHA; +#endif + funcs->glTexSubImage2D(GL_TEXTURE_2D, 0, tx, ty, imgWidth, imgHeight, format, GL_UNSIGNED_BYTE, img.constBits()); + } +} + +static void load_glyph_image_region_to_texture(QOpenGLContext *ctx, + const QImage &srcImg, + int x, int y, + int w, int h, + GLuint texture, + int tx, int ty) +{ + Q_ASSERT(x + w <= srcImg.width() && y + h <= srcImg.height()); + + QImage img; + if (x != 0 || y != 0 || w != srcImg.width() || h != srcImg.height()) + img = srcImg.copy(x, y, w, h); + else + img = srcImg; + + load_glyph_image_to_texture(ctx, img, texture, tx, ty); +} + void QOpenGLTextureGlyphCache::resizeTextureData(int width, int height) { QOpenGLContext *ctx = QOpenGLContext::currentContext(); @@ -207,9 +295,8 @@ void QOpenGLTextureGlyphCache::resizeTextureData(int width, int height) if (ctx->d_func()->workaround_brokenFBOReadBack) { QImageTextureGlyphCache::resizeTextureData(width, height); - Q_ASSERT(image().depth() == 8); - funcs->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, oldHeight, GL_ALPHA, GL_UNSIGNED_BYTE, image().constBits()); - funcs->glDeleteTextures(1, &oldTexture); + load_glyph_image_region_to_texture(ctx, image(), 0, 0, qMin(oldWidth, width), qMin(oldHeight, height), + m_textureResource->m_texture, 0, 0); return; } @@ -336,88 +423,14 @@ void QOpenGLTextureGlyphCache::fillTexture(const Coord &c, glyph_t glyph, QFixed return; } - QOpenGLFunctions *funcs = ctx->functions(); if (ctx->d_func()->workaround_brokenFBOReadBack) { QImageTextureGlyphCache::fillTexture(c, glyph, subPixelPosition); - - funcs->glBindTexture(GL_TEXTURE_2D, m_textureResource->m_texture); - const QImage &texture = image(); - const uchar *bits = texture.constBits(); - bits += c.y * texture.bytesPerLine() + c.x; - for (int i=0; i<c.h; ++i) { - funcs->glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y + i, c.w, 1, GL_ALPHA, GL_UNSIGNED_BYTE, bits); - bits += texture.bytesPerLine(); - } + load_glyph_image_region_to_texture(ctx, image(), c.x, c.y, c.w, c.h, m_textureResource->m_texture, c.x, c.y); return; } QImage mask = textureMapForGlyph(glyph, subPixelPosition); - const int maskWidth = mask.width(); - const int maskHeight = mask.height(); - - if (mask.format() == QImage::Format_Mono) { - mask = mask.convertToFormat(QImage::Format_Indexed8); - for (int y = 0; y < maskHeight; ++y) { - uchar *src = (uchar *) mask.scanLine(y); - for (int x = 0; x < maskWidth; ++x) - src[x] = -src[x]; // convert 0 and 1 into 0 and 255 - } - } else if (mask.depth() == 32) { - if (mask.format() == QImage::Format_RGB32 - // We need to make the alpha component equal to the average of the RGB values. - // This is needed when drawing sub-pixel antialiased text on translucent targets. -#if Q_BYTE_ORDER == Q_BIG_ENDIAN - || mask.format() == QImage::Format_ARGB32_Premultiplied -#else - || (mask.format() == QImage::Format_ARGB32_Premultiplied - && ctx->isOpenGLES()) -#endif - ) { - for (int y = 0; y < maskHeight; ++y) { - QRgb *src = (QRgb *) mask.scanLine(y); - for (int x = 0; x < maskWidth; ++x) { - int r = qRed(src[x]); - int g = qGreen(src[x]); - int b = qBlue(src[x]); - int avg; - if (mask.format() == QImage::Format_RGB32) - avg = (r + g + b + 1) / 3; // "+1" for rounding. - else // Format_ARGB_Premultiplied - avg = qAlpha(src[x]); - - src[x] = qRgba(r, g, b, avg); - // swizzle the bits to accommodate for the GL_RGBA upload. -#if Q_BYTE_ORDER != Q_BIG_ENDIAN - if (ctx->isOpenGLES()) -#endif - src[x] = ARGB2RGBA(src[x]); - } - } - } - } - - funcs->glBindTexture(GL_TEXTURE_2D, m_textureResource->m_texture); - if (mask.depth() == 32) { -#ifdef QT_OPENGL_ES_2 - GLenum fmt = GL_RGBA; -#else - GLenum fmt = ctx->isOpenGLES() ? GL_RGBA : GL_BGRA; -#endif // QT_OPENGL_ES_2 - -#if Q_BYTE_ORDER == Q_BIG_ENDIAN - fmt = GL_RGBA; -#endif - funcs->glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y, maskWidth, maskHeight, fmt, GL_UNSIGNED_BYTE, mask.bits()); - } else { - // The scanlines in mask are 32-bit aligned, even for mono or 8-bit formats. This - // is good because it matches the default of 4 bytes for GL_UNPACK_ALIGNMENT. -#if !defined(QT_OPENGL_ES_2) - const GLenum format = isCoreProfile() ? GL_RED : GL_ALPHA; -#else - const GLenum format = GL_ALPHA; -#endif - funcs->glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y, maskWidth, maskHeight, format, GL_UNSIGNED_BYTE, mask.bits()); - } + load_glyph_image_to_texture(ctx, mask, m_textureResource->m_texture, c.x, c.y); } int QOpenGLTextureGlyphCache::glyphPadding() const diff --git a/src/gui/painting/qoutlinemapper.cpp b/src/gui/painting/qoutlinemapper.cpp index 7245b44fc7..f1e4ce5820 100644 --- a/src/gui/painting/qoutlinemapper.cpp +++ b/src/gui/painting/qoutlinemapper.cpp @@ -78,10 +78,23 @@ void QOutlineMapper::curveTo(const QPointF &cp1, const QPointF &cp2, const QPoin #endif QBezier bezier = QBezier::fromPoints(m_elements.last(), cp1, cp2, ep); - bezier.addToPolygon(m_elements, m_curve_threshold); - m_element_types.reserve(m_elements.size()); - for (int i = m_elements.size() - m_element_types.size(); i; --i) - m_element_types << QPainterPath::LineToElement; + + bool outsideClip = false; + // Test one point first before doing a full intersection test. + if (!QRectF(m_clip_rect).contains(m_transform.map(ep))) { + QRectF potentialCurveArea = m_transform.mapRect(bezier.bounds()); + outsideClip = !potentialCurveArea.intersects(m_clip_rect); + } + if (outsideClip) { + // The curve is entirely outside the clip rect, so just + // approximate it with a line that closes the path. + lineTo(ep); + } else { + bezier.addToPolygon(m_elements, m_curve_threshold); + m_element_types.reserve(m_elements.size()); + for (int i = m_elements.size() - m_element_types.size(); i; --i) + m_element_types << QPainterPath::LineToElement; + } Q_ASSERT(m_elements.size() == m_element_types.size()); } diff --git a/src/gui/painting/qpaintengine.cpp b/src/gui/painting/qpaintengine.cpp index d59d843f8a..020392409d 100644 --- a/src/gui/painting/qpaintengine.cpp +++ b/src/gui/painting/qpaintengine.cpp @@ -310,6 +310,7 @@ struct QT_Point { int x; int y; }; +Q_DECLARE_TYPEINFO(QT_Point, Q_PRIMITIVE_TYPE); /*! \fn void QPaintEngine::drawPolygon(const QPointF *points, int pointCount, @@ -340,6 +341,8 @@ struct QT_PointF { qreal x; qreal y; }; +Q_DECLARE_TYPEINFO(QT_PointF, Q_PRIMITIVE_TYPE); + /*! \overload diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp index e70079915a..aeb76589e0 100644 --- a/src/gui/painting/qpainter.cpp +++ b/src/gui/painting/qpainter.cpp @@ -6183,7 +6183,8 @@ static QPixmap generateWavyPixmap(qreal maxRadius, const QPen &pen) QString key = QLatin1String("WaveUnderline-") % pen.color().name() - % HexString<qreal>(radiusBase); + % HexString<qreal>(radiusBase) + % HexString<qreal>(pen.widthF()); QPixmap pixmap; if (QPixmapCache::find(key, pixmap)) @@ -6191,7 +6192,7 @@ static QPixmap generateWavyPixmap(qreal maxRadius, const QPen &pen) const qreal halfPeriod = qMax(qreal(2), qreal(radiusBase * 1.61803399)); // the golden ratio const int width = qCeil(100 / (2 * halfPeriod)) * (2 * halfPeriod); - const int radius = qFloor(radiusBase); + const qreal radius = qFloor(radiusBase * 2) / 2.; QPainterPath path; @@ -6214,7 +6215,7 @@ static QPixmap generateWavyPixmap(qreal maxRadius, const QPen &pen) // due to it having a rather thick width for the regular underline. const qreal maxPenWidth = .8 * radius; if (wavePen.widthF() > maxPenWidth) - wavePen.setWidth(maxPenWidth); + wavePen.setWidthF(maxPenWidth); QPainter imgPainter(&pixmap); imgPainter.setPen(wavePen); @@ -6254,9 +6255,6 @@ static void drawTextItemDecoration(QPainter *painter, const QPointF &pos, const painter->setRenderHint(QPainter::Qt4CompatiblePainting, false); const qreal underlineOffset = fe->underlinePosition().toReal(); - // deliberately ceil the offset to avoid the underline coming too close to - // the text above it. - const qreal underlinePos = pos.y() + qCeil(underlineOffset) + 0.5; if (underlineStyle == QTextCharFormat::SpellCheckUnderline) { QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme(); @@ -6267,19 +6265,26 @@ static void drawTextItemDecoration(QPainter *painter, const QPointF &pos, const if (underlineStyle == QTextCharFormat::WaveUnderline) { painter->save(); painter->translate(0, pos.y() + 1); + qreal maxHeight = fe->descent().toReal() - qreal(1); QColor uc = charFormat.underlineColor(); if (uc.isValid()) pen.setColor(uc); // Adapt wave to underlineOffset or pen width, whatever is larger, to make it work on all platforms - const QPixmap wave = generateWavyPixmap(qMax(underlineOffset, pen.widthF()), pen); - const int descent = (int) fe->descent().toReal(); + const QPixmap wave = generateWavyPixmap(qMin(qMax(underlineOffset, pen.widthF()), maxHeight / 2.), pen); + const int descent = qFloor(maxHeight); painter->setBrushOrigin(painter->brushOrigin().x(), 0); painter->fillRect(pos.x(), 0, qCeil(width), qMin(wave.height(), descent), wave); painter->restore(); } else if (underlineStyle != QTextCharFormat::NoUnderline) { + // Deliberately ceil the offset to avoid the underline coming too close to + // the text above it, but limit it to stay within descent. + qreal adjustedUnderlineOffset = std::ceil(underlineOffset) + 0.5; + if (underlineOffset <= fe->descent().toReal()) + adjustedUnderlineOffset = qMin(adjustedUnderlineOffset, fe->descent().toReal() - 0.5); + const qreal underlinePos = pos.y() + adjustedUnderlineOffset; QColor uc = charFormat.underlineColor(); if (uc.isValid()) pen.setColor(uc); diff --git a/src/gui/painting/qpainter.h b/src/gui/painting/qpainter.h index bd745e41ba..5743d97405 100644 --- a/src/gui/painting/qpainter.h +++ b/src/gui/painting/qpainter.h @@ -486,6 +486,7 @@ private: friend class QPreviewPaintEngine; friend class QTextEngine; }; +Q_DECLARE_TYPEINFO(QPainter::PixmapFragment, Q_RELOCATABLE_TYPE); Q_DECLARE_OPERATORS_FOR_FLAGS(QPainter::RenderHints) diff --git a/src/gui/painting/qpdf.cpp b/src/gui/painting/qpdf.cpp index 02f9be197f..b014305796 100644 --- a/src/gui/painting/qpdf.cpp +++ b/src/gui/painting/qpdf.cpp @@ -1201,7 +1201,7 @@ void QPdfEngine::setPen() switch(d->pen.joinStyle()) { case Qt::MiterJoin: case Qt::SvgMiterJoin: - *d->currentPage << d->pen.miterLimit() << "M "; + *d->currentPage << qMax(qreal(1.0), d->pen.miterLimit()) << "M "; pdfJoinStyle = 0; break; case Qt::BevelJoin: @@ -2214,8 +2214,10 @@ int QPdfEnginePrivate::generateGradientShader(const QGradient *gradient, const Q case QGradient::RadialGradient: return generateRadialGradientShader(static_cast<const QRadialGradient *>(gradient), matrix, alpha); case QGradient::ConicalGradient: - default: - qWarning("Implement me!"); + Q_UNIMPLEMENTED(); // ### Implement me! + break; + case QGradient::NoGradient: + break; } return 0; } diff --git a/src/gui/painting/qregion.cpp b/src/gui/painting/qregion.cpp index abdfc7c8c7..b39a23e7f2 100644 --- a/src/gui/painting/qregion.cpp +++ b/src/gui/painting/qregion.cpp @@ -1139,7 +1139,15 @@ void addSegmentsToPath(Segment *segment, QPainterPath &path) } } -} +} // unnamed namespace + +// the following is really a lie, because Segments cannot be relocated, as they +// reference each other by address. For the same reason, they aren't even copyable, +// but the code works with the compiler-generated (wrong) copy and move special +// members, so use this as an optimization. The only container these are used in +// (a QVarLengthArray in qt_regionToPath()) is resized once up-front, so doesn't +// have a problem with this, but benefits from not having to run Segment ctors: +Q_DECLARE_TYPEINFO(Segment, Q_PRIMITIVE_TYPE); Q_GUI_EXPORT QPainterPath qt_regionToPath(const QRegion ®ion) { diff --git a/src/gui/painting/qrgba64.h b/src/gui/painting/qrgba64.h index a87d3fbb35..27e1e6e06d 100644 --- a/src/gui/painting/qrgba64.h +++ b/src/gui/painting/qrgba64.h @@ -103,7 +103,7 @@ public: Q_DECL_RELAXED_CONSTEXPR static QRgba64 fromArgb32(uint rgb) { - return fromRgba(rgb >> 16, rgb >> 8, rgb, rgb >> 24); + return fromRgba(quint8(rgb >> 16), quint8(rgb >> 8), quint8(rgb), quint8(rgb >> 24)); } Q_DECL_CONSTEXPR bool isOpaque() const @@ -115,10 +115,10 @@ public: return (rgba & alphaMask()) == 0; } - Q_DECL_CONSTEXPR quint16 red() const { return rgba >> RedShift; } - Q_DECL_CONSTEXPR quint16 green() const { return rgba >> GreenShift; } - Q_DECL_CONSTEXPR quint16 blue() const { return rgba >> BlueShift; } - Q_DECL_CONSTEXPR quint16 alpha() const { return rgba >> AlphaShift; } + Q_DECL_CONSTEXPR quint16 red() const { return quint16(rgba >> RedShift); } + Q_DECL_CONSTEXPR quint16 green() const { return quint16(rgba >> GreenShift); } + Q_DECL_CONSTEXPR quint16 blue() const { return quint16(rgba >> BlueShift); } + Q_DECL_CONSTEXPR quint16 alpha() const { return quint16(rgba >> AlphaShift); } void setRed(quint16 _red) { rgba = (rgba & ~(Q_UINT64_C(0xffff) << RedShift)) | (quint64(_red) << RedShift); } void setGreen(quint16 _green) { rgba = (rgba & ~(Q_UINT64_C(0xffff) << GreenShift)) | (quint64(_green) << GreenShift); } void setBlue(quint16 _blue) { rgba = (rgba & ~(Q_UINT64_C(0xffff) << BlueShift)) | (quint64(_blue) << BlueShift); } @@ -130,11 +130,11 @@ public: Q_DECL_CONSTEXPR quint8 alpha8() const { return div_257(alpha()); } Q_DECL_CONSTEXPR uint toArgb32() const { - return (alpha8() << 24) | (red8() << 16) | (green8() << 8) | blue8(); + return uint((alpha8() << 24) | (red8() << 16) | (green8() << 8) | blue8()); } Q_DECL_CONSTEXPR ushort toRgb16() const { - return (red() & 0xf800) | ((green() >> 10) << 5) | (blue() >> 11); + return ushort((red() & 0xf800) | ((green() >> 10) << 5) | (blue() >> 11)); } Q_DECL_RELAXED_CONSTEXPR QRgba64 premultiplied() const @@ -143,7 +143,7 @@ public: const quint16 r = div_65535(red() * a); const quint16 g = div_65535(green() * a); const quint16 b = div_65535(blue() * a); - return fromRgba64(r, g, b, a); + return fromRgba64(r, g, b, quint16(a)); } Q_DECL_RELAXED_CONSTEXPR QRgba64 unpremultiplied() const @@ -169,18 +169,18 @@ public: private: static Q_DECL_CONSTEXPR Q_ALWAYS_INLINE quint64 alphaMask() { return Q_UINT64_C(0xffff) << AlphaShift; } - static Q_DECL_CONSTEXPR Q_ALWAYS_INLINE uint div_257_floor(uint x) { return (x - (x >> 8)) >> 8; } - static Q_DECL_CONSTEXPR Q_ALWAYS_INLINE uint div_257(uint x) { return div_257_floor(x + 128); } - static Q_DECL_CONSTEXPR Q_ALWAYS_INLINE uint div_65535(uint x) { return (x + (x>>16) + 0x8000U) >> 16; } + static Q_DECL_CONSTEXPR Q_ALWAYS_INLINE quint8 div_257_floor(uint x) { return quint8((x - (x >> 8)) >> 8); } + static Q_DECL_CONSTEXPR Q_ALWAYS_INLINE quint8 div_257(quint16 x) { return div_257_floor(x + 128U); } + static Q_DECL_CONSTEXPR Q_ALWAYS_INLINE quint16 div_65535(uint x) { return quint16((x + (x>>16) + 0x8000U) >> 16); } Q_DECL_RELAXED_CONSTEXPR Q_ALWAYS_INLINE QRgba64 unpremultiplied_32bit() const { if (isOpaque() || isTransparent()) return *this; const quint32 a = alpha(); - const quint16 r = (quint32(red()) * 0xffff + a/2) / a; - const quint16 g = (quint32(green()) * 0xffff + a/2) / a; - const quint16 b = (quint32(blue()) * 0xffff + a/2) / a; - return fromRgba64(r, g, b, a); + const quint16 r = quint16((red() * 0xffff + a/2) / a); + const quint16 g = quint16((green() * 0xffff + a/2) / a); + const quint16 b = quint16((blue() * 0xffff + a/2) / a); + return fromRgba64(r, g, b, quint16(a)); } Q_DECL_RELAXED_CONSTEXPR Q_ALWAYS_INLINE QRgba64 unpremultiplied_64bit() const { @@ -188,10 +188,10 @@ private: return *this; const quint64 a = alpha(); const quint64 fa = (Q_UINT64_C(0xffff00008000) + a/2) / a; - const quint16 r = (red() * fa + 0x80000000) >> 32; - const quint16 g = (green() * fa + 0x80000000) >> 32; - const quint16 b = (blue() * fa + 0x80000000) >> 32; - return fromRgba64(r, g, b, a); + const quint16 r = quint16((red() * fa + 0x80000000) >> 32); + const quint16 g = quint16((green() * fa + 0x80000000) >> 32); + const quint16 b = quint16((blue() * fa + 0x80000000) >> 32); + return fromRgba64(r, g, b, quint16(a)); } }; diff --git a/src/gui/text/qcssparser.cpp b/src/gui/text/qcssparser.cpp index 4782ef5e20..bb92e6bfba 100644 --- a/src/gui/text/qcssparser.cpp +++ b/src/gui/text/qcssparser.cpp @@ -67,6 +67,7 @@ struct QCssKnownValue static const QCssKnownValue properties[NumProperties - 1] = { { "-qt-background-role", QtBackgroundRole }, { "-qt-block-indent", QtBlockIndent }, + { "-qt-line-height-type", QtLineHeightType }, { "-qt-list-indent", QtListIndent }, { "-qt-list-number-prefix", QtListNumberPrefix }, { "-qt-list-number-suffix", QtListNumberSuffix }, diff --git a/src/gui/text/qcssparser_p.h b/src/gui/text/qcssparser_p.h index b7692d32aa..4da7b62dba 100644 --- a/src/gui/text/qcssparser_p.h +++ b/src/gui/text/qcssparser_p.h @@ -189,6 +189,7 @@ enum Property { QtListNumberPrefix, QtListNumberSuffix, LineHeight, + QtLineHeightType, NumProperties }; diff --git a/src/gui/text/qfontdatabase.cpp b/src/gui/text/qfontdatabase.cpp index 629a098fb7..e4c9b45dc2 100644 --- a/src/gui/text/qfontdatabase.cpp +++ b/src/gui/text/qfontdatabase.cpp @@ -2669,7 +2669,7 @@ QFontEngine *QFontDatabase::findFont(const QFontDef &request, int script) QtFontDesc desc; QList<int> blackListed; - int index = match(script, request, family_name, foundry_name, &desc, blackListed); + int index = match(multi ? QChar::Script_Common : script, request, family_name, foundry_name, &desc, blackListed); if (index >= 0) { engine = loadEngine(script, request, desc.family, desc.foundry, desc.style, desc.size); if (engine) @@ -2702,7 +2702,7 @@ QFontEngine *QFontDatabase::findFont(const QFontDef &request, int script) if (!engine) { QtFontDesc desc; do { - index = match(script, def, def.family, QLatin1String(""), &desc, blackListed); + index = match(multi ? QChar::Script_Common : script, def, def.family, QLatin1String(""), &desc, blackListed); if (index >= 0) { QFontDef loadDef = def; if (loadDef.family.isEmpty()) diff --git a/src/gui/text/qfontdatabase.h b/src/gui/text/qfontdatabase.h index 36e5677b92..67cf671304 100644 --- a/src/gui/text/qfontdatabase.h +++ b/src/gui/text/qfontdatabase.h @@ -167,6 +167,7 @@ private: friend class QFontDialog; friend class QFontDialogPrivate; friend class QFontEngineMulti; + friend class QRawFont; QFontDatabasePrivate *d; }; diff --git a/src/gui/text/qfontengine_ft.cpp b/src/gui/text/qfontengine_ft.cpp index 86fb0e8ae4..e89522f5af 100644 --- a/src/gui/text/qfontengine_ft.cpp +++ b/src/gui/text/qfontengine_ft.cpp @@ -52,6 +52,7 @@ #include <qscopedvaluerollback.h> #include "qthreadstorage.h" #include <qmath.h> +#include <qendian.h> #include <ft2build.h> #include FT_FREETYPE_H @@ -192,6 +193,15 @@ int QFreetypeFace::getPointInOutline(glyph_t glyph, int flags, quint32 point, QF 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); /* @@ -249,6 +259,7 @@ QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id, newFreetype->ref.store(1); newFreetype->xsize = 0; newFreetype->ysize = 0; + newFreetype->scalableBitmapScaleFactor = 1; newFreetype->matrix.xx = 0x10000; newFreetype->matrix.yy = 0x10000; newFreetype->matrix.xy = 0; @@ -330,36 +341,46 @@ void QFreetypeFace::computeSize(const QFontDef &fontDef, int *xsize, int *ysize, *xsize = *ysize * fontDef.stretch / 100; *outline_drawing = false; - /* - * Bitmap only faces must match exactly, so find the closest - * one (height dominant search) - */ if (!(face->face_flags & FT_FACE_FLAG_SCALABLE)) { int best = 0; - 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; + 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; + } } } - if (FT_Set_Char_Size(face, face->available_sizes[best].x_ppem, face->available_sizes[best].y_ppem, 0, 0) == 0) { + + // 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 { - int err = 1; - if (!(face->face_flags & FT_FACE_FLAG_SCALABLE) && ysize == 0 && face->num_fixed_sizes >= 1) { - // work around FT 2.1.10 problem with BDF without PIXEL_SIZE property - err = FT_Set_Pixel_Sizes(face, face->available_sizes[0].width, face->available_sizes[0].height); - if (err && face->num_fixed_sizes == 1) - err = 0; //even more of a workaround... - } - - if (err) - *xsize = *ysize = 0; + *xsize = *ysize = 0; } } else { *outline_drawing = (*xsize > (QT_MAX_CACHED_GLYPH_SIZE<<6) || *ysize > (QT_MAX_CACHED_GLYPH_SIZE<<6)); @@ -736,6 +757,11 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format, 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; @@ -829,6 +855,10 @@ int QFontEngineFT::loadFlags(QGlyphSet *set, GlyphFormat format, int flags, 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) @@ -849,11 +879,8 @@ static inline bool areMetricsTooLarge(const QFontEngineFT::GlyphInfo &info) { // false if exceeds QFontEngineFT::Glyph metrics return (short)(info.linearAdvance) != info.linearAdvance - || (signed char)(info.xOff) != info.xOff || (uchar)(info.width) != info.width - || (uchar)(info.height) != info.height - || (signed char)(info.x) != info.x - || (signed char)(info.y) != info.y; + || (uchar)(info.height) != info.height; } QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, @@ -893,7 +920,7 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, || matrix.xy != 0 || matrix.yx != 0; - if (transform || (format != Format_Mono && !embeddedbitmap)) + if (transform || (format != Format_Mono && !isScalableBitmap())) load_flags |= FT_LOAD_NO_BITMAP; FT_Error err = FT_Load_Glyph(face, glyph, load_flags); @@ -1124,7 +1151,11 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, 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; @@ -1135,7 +1166,7 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, dst += pitch; src += slot->bitmap.pitch; } - } else { + } else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) { if (hsubpixel) { while (h--) { uint *dd = (uint *)dst; @@ -1169,6 +1200,29 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, } } } +#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<int>(slot->bitmap.width); x++, srcPixel++, dstPixel++) { + const quint32 pixel = *srcPixel; + *dstPixel = qbswap(pixel); + } +#else + memcpy(dst, src, slot->bitmap.width * 4); +#endif + dst += slot->bitmap.pitch; + src += slot->bitmap.pitch; + } + 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; @@ -1239,46 +1293,68 @@ int QFontEngineFT::synthesized() const QFixed QFontEngineFT::ascent() const { - return QFixed::fromFixed(metrics.ascender); + QFixed v = QFixed::fromFixed(metrics.ascender); + if (freetype->scalableBitmapScaleFactor != 1) + v *= freetype->scalableBitmapScaleFactor; + return v; } QFixed QFontEngineFT::descent() const { - return QFixed::fromFixed(-metrics.descender); + QFixed v = QFixed::fromFixed(-metrics.descender); + if (freetype->scalableBitmapScaleFactor != 1) + v *= freetype->scalableBitmapScaleFactor; + return v; } QFixed QFontEngineFT::leading() const { - return QFixed::fromFixed(metrics.height - metrics.ascender + metrics.descender); + QFixed v = QFixed::fromFixed(metrics.height - metrics.ascender + metrics.descender); + if (freetype->scalableBitmapScaleFactor != 1) + v *= freetype->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; + if (!isScalableBitmap()) { + 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; + } + } else { + return QFixed(freetype->face->size->metrics.y_ppem) * freetype->scalableBitmapScaleFactor; } 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(); + if (!isScalableBitmap()) { + 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; + } + } else { + const qreal aspectRatio = (qreal)xsize / ysize; + return QFixed::fromReal(fontDef.pixelSize * aspectRatio); + } + + return QFontEngine::averageCharWidth(); } qreal QFontEngineFT::maxCharWidth() const { - return metrics.max_advance >> 6; + QFixed max_advance = QFixed::fromFixed(metrics.max_advance); + if (freetype->scalableBitmapScaleFactor != 1) + max_advance *= freetype->scalableBitmapScaleFactor; + return max_advance.toReal(); } QFixed QFontEngineFT::lineThickness() const @@ -1561,6 +1637,23 @@ bool QFontEngineFT::shouldUseDesignMetrics(QFontEngine::ShaperFlags flags) const return default_hint_style == HintNone || default_hint_style == HintLight || (flags & DesignMetrics); } +QFixed QFontEngineFT::scaledBitmapMetrics(QFixed m) const +{ + return m * freetype->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; @@ -1583,6 +1676,9 @@ void QFontEngineFT::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlag if (!cacheEnabled && g != &emptyGlyph) delete g; } + + if (freetype->scalableBitmapScaleFactor != 1) + glyphs->advances[i] *= freetype->scalableBitmapScaleFactor; } if (face) unlockFace(); @@ -1599,8 +1695,13 @@ glyph_metrics_t QFontEngineFT::boundingBox(const QGlyphLayout &glyphs) glyph_metrics_t overall; // initialize with line height, we get the same behaviour on all platforms - overall.y = -ascent(); - overall.height = ascent() + descent(); + 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; @@ -1642,6 +1743,8 @@ glyph_metrics_t QFontEngineFT::boundingBox(const QGlyphLayout &glyphs) if (face) unlockFace(); + if (isScalableBitmap()) + overall = scaledBitmapMetrics(overall); return overall; } @@ -1678,6 +1781,9 @@ glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph) } if (face) unlockFace(); + + if (isScalableBitmap()) + overall = scaledBitmapMetrics(overall); return overall; } @@ -1713,6 +1819,9 @@ glyph_metrics_t QFontEngineFT::alphaMapBoundingBox(glyph_t glyph, QFixed subPixe overall.xoff = TRUNC(ROUND(face->glyph->advance.x)); unlockFace(); } + + if (isScalableBitmap()) + overall = scaledBitmapMetrics(overall); return overall; } @@ -1860,6 +1969,31 @@ QImage QFontEngineFT::alphaRGBMapForGlyph(glyph_t g, QFixed subPixelPosition, co 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() || freetype->scalableBitmapScaleFactor != 1)) { + QTransform trans(t); + const qreal scaleFactor = freetype->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); @@ -1881,9 +2015,10 @@ FT_Face QFontEngineFT::lockFace(Scaling scale) const freetype->lock(); FT_Face face = freetype->face; if (scale == Unscaled) { - FT_Set_Char_Size(face, face->units_per_EM << 6, face->units_per_EM << 6, 0, 0); - freetype->xsize = face->units_per_EM << 6; - freetype->ysize = face->units_per_EM << 6; + 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; diff --git a/src/gui/text/qfontengine_ft_p.h b/src/gui/text/qfontengine_ft_p.h index 3421c873d5..3f4bf84753 100644 --- a/src/gui/text/qfontengine_ft_p.h +++ b/src/gui/text/qfontengine_ft_p.h @@ -96,6 +96,7 @@ public: FT_Face face; int xsize; // 26.6 int ysize; // 26.6 + QFixed scalableBitmapScaleFactor; FT_Matrix matrix; FT_CharMap unicode_map; FT_CharMap symbol_map; @@ -107,6 +108,8 @@ public: 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); @@ -140,9 +143,9 @@ public: short linearAdvance; unsigned char width; unsigned char height; - signed char x; - signed char y; - signed char advance; + short x; + short y; + short advance; signed char format; uchar *data; }; @@ -239,6 +242,7 @@ private: 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, @@ -266,6 +270,7 @@ private: 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) const { return loadGlyph(cacheEnabled ? &defaultGlyphSet : 0, glyph, subPixelPosition, format, fetchMetricsOnly); } @@ -316,6 +321,8 @@ private: 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; diff --git a/src/gui/text/qrawfont.cpp b/src/gui/text/qrawfont.cpp index 2a53b8869d..19ac4f1dbc 100644 --- a/src/gui/text/qrawfont.cpp +++ b/src/gui/text/qrawfont.cpp @@ -700,6 +700,20 @@ QRawFont QRawFont::fromFont(const QFont &font, QFontDatabase::WritingSystem writ if (fe != 0 && fe->type() == QFontEngine::Multi) { QFontEngineMulti *multiEngine = static_cast<QFontEngineMulti *>(fe); fe = multiEngine->engine(0); + + if (script > QChar::Script_Latin) { + // keep in sync with QFontEngineMulti::loadEngine() + QFontDef request(multiEngine->fontDef); + request.styleStrategy |= QFont::NoFontMerging; + + if (QFontEngine *engine = QFontDatabase::findFont(request, script)) { + if (request.weight > QFont::Normal) + engine->fontDef.weight = request.weight; + if (request.style > QFont::StyleNormal) + engine->fontDef.style = request.style; + fe = engine; + } + } Q_ASSERT(fe); } diff --git a/src/gui/text/qtextdocument.cpp b/src/gui/text/qtextdocument.cpp index 3a08176044..8e4fa8a730 100644 --- a/src/gui/text/qtextdocument.cpp +++ b/src/gui/text/qtextdocument.cpp @@ -2763,26 +2763,25 @@ void QTextHtmlExporter::emitBlockAttributes(const QTextBlock &block) } if (format.lineHeightType() != QTextBlockFormat::SingleHeight) { + html += QLatin1String(" line-height:") + + QString::number(format.lineHeight()); switch (format.lineHeightType()) { case QTextBlockFormat::ProportionalHeight: + html += QLatin1String("%;"); + break; case QTextBlockFormat::FixedHeight: - html += QLatin1String(" line-height:"); + html += QLatin1String("; -qt-line-height-type: fixed;"); break; case QTextBlockFormat::MinimumHeight: - html += QLatin1String(" min-height:"); + html += QLatin1String("px;"); break; case QTextBlockFormat::LineDistanceHeight: - html += QLatin1String(" line-spacing:"); + html += QLatin1String("; -qt-line-height-type: line-distance;"); break; - case QTextBlockFormat::SingleHeight: default: + html += QLatin1String(";"); break; // Should never reach here } - html += QString::number(format.lineHeight()); - if (format.lineHeightType() == QTextBlockFormat::ProportionalHeight) - html += QLatin1String("%;"); - else - html += QLatin1String("px;"); } emitPageBreakPolicy(format.pageBreakPolicy()); diff --git a/src/gui/text/qtextengine_p.h b/src/gui/text/qtextengine_p.h index ab08f2d5e5..56c9825cc1 100644 --- a/src/gui/text/qtextengine_p.h +++ b/src/gui/text/qtextengine_p.h @@ -180,6 +180,7 @@ struct QGlyphAttributes { uchar reserved : 2; }; Q_STATIC_ASSERT(sizeof(QGlyphAttributes) == 1); +Q_DECLARE_TYPEINFO(QGlyphAttributes, Q_PRIMITIVE_TYPE); struct QGlyphLayout { diff --git a/src/gui/text/qtexthtmlparser.cpp b/src/gui/text/qtexthtmlparser.cpp index d8e12f7024..576ff7d935 100644 --- a/src/gui/text/qtexthtmlparser.cpp +++ b/src/gui/text/qtexthtmlparser.cpp @@ -492,7 +492,7 @@ static QString quoteNewline(const QString &s) QTextHtmlParserNode::QTextHtmlParserNode() : parent(0), id(Html_unknown), - cssFloat(QTextFrameFormat::InFlow), hasOwnListStyle(false), + cssFloat(QTextFrameFormat::InFlow), hasOwnListStyle(false), hasOwnLineHeightType(false), hasCssListIndent(false), isEmptyParagraph(false), isTextFrame(false), isRootFrame(false), displayMode(QTextHtmlElement::DisplayInline), hasHref(false), listStyle(QTextListFormat::ListStyleUndefined), imageWidth(-1), imageHeight(-1), tableBorder(0), @@ -1198,20 +1198,48 @@ void QTextHtmlParserNode::applyCssDeclarations(const QVector<QCss::Declaration> case QCss::QtBlockIndent: blockFormat.setIndent(decl.d->values.first().variant.toInt()); break; - case QCss::LineHeight: { + case QCss::QtLineHeightType: { + QString lineHeightTypeName = decl.d->values.first().variant.toString(); + QTextBlockFormat::LineHeightTypes lineHeightType; + if (lineHeightTypeName.compare(QLatin1String("proportional"), Qt::CaseInsensitive) == 0) + lineHeightType = QTextBlockFormat::ProportionalHeight; + else if (lineHeightTypeName.compare(QLatin1String("fixed"), Qt::CaseInsensitive) == 0) + lineHeightType = QTextBlockFormat::FixedHeight; + else if (lineHeightTypeName.compare(QLatin1String("minimum"), Qt::CaseInsensitive) == 0) + lineHeightType = QTextBlockFormat::MinimumHeight; + else if (lineHeightTypeName.compare(QLatin1String("line-distance"), Qt::CaseInsensitive) == 0) + lineHeightType = QTextBlockFormat::LineDistanceHeight; + else + lineHeightType = QTextBlockFormat::SingleHeight; + + blockFormat.setProperty(QTextBlockFormat::LineHeightType, lineHeightType); + hasOwnLineHeightType = true; + } + break; + case QCss::LineHeight: { qreal lineHeight; + QTextBlockFormat::LineHeightTypes lineHeightType; if (decl.realValue(&lineHeight, "px")) { - blockFormat.setLineHeight(lineHeight, QTextBlockFormat::FixedHeight); + lineHeightType = QTextBlockFormat::MinimumHeight; } else { bool ok; QString value = decl.d->values.first().toString(); lineHeight = value.toDouble(&ok); - if (ok) - blockFormat.setLineHeight(lineHeight, QTextBlockFormat::ProportionalHeight); - else - blockFormat.setLineHeight(0, QTextBlockFormat::SingleHeight); + if (ok) { + lineHeightType = QTextBlockFormat::ProportionalHeight; + } else { + lineHeight = 0.0; + lineHeightType = QTextBlockFormat::SingleHeight; + } } - break; } + + // Only override line height type if specified in same node + if (hasOwnLineHeightType) + lineHeightType = QTextBlockFormat::LineHeightTypes(blockFormat.lineHeightType()); + + blockFormat.setLineHeight(lineHeight, lineHeightType); + break; + } case QCss::TextIndent: { qreal indent = 0; if (decl.realValue(&indent, "px")) diff --git a/src/gui/text/qtexthtmlparser_p.h b/src/gui/text/qtexthtmlparser_p.h index 8e5a90be0b..4c0dd967f9 100644 --- a/src/gui/text/qtexthtmlparser_p.h +++ b/src/gui/text/qtexthtmlparser_p.h @@ -171,6 +171,7 @@ struct QTextHtmlParserNode { QTextBlockFormat blockFormat; uint cssFloat : 2; uint hasOwnListStyle : 1; + uint hasOwnLineHeightType : 1; uint hasCssListIndent : 1; uint isEmptyParagraph : 1; uint isTextFrame : 1; diff --git a/src/gui/text/qtextobject.cpp b/src/gui/text/qtextobject.cpp index a756549f3e..e2130a09d9 100644 --- a/src/gui/text/qtextobject.cpp +++ b/src/gui/text/qtextobject.cpp @@ -418,9 +418,12 @@ QTextFrame::QTextFrame(QTextDocument *doc) { } -// ### DOC: What does this do to child frames? /*! - Destroys the frame, and removes it from the document's layout. + Destroys the text frame. + + \warning Text frames are owned by the document, so you should + never destroy them yourself. In order to remove a frame from + its document, remove its contents using a \c QTextCursor. */ QTextFrame::~QTextFrame() { diff --git a/src/gui/text/qzip.cpp b/src/gui/text/qzip.cpp index 5178f5a9a8..7cb89543ba 100644 --- a/src/gui/text/qzip.cpp +++ b/src/gui/text/qzip.cpp @@ -822,12 +822,12 @@ void QZipWriterPrivate::addEntry(EntryType type, const QString &fileName, const QZipReader::QZipReader(const QString &archive, QIODevice::OpenMode mode) { QScopedPointer<QFile> f(new QFile(archive)); - f->open(mode); + const bool result = f->open(mode); QZipReader::Status status; const QFileDevice::FileError error = f->error(); - if (error == QFile::NoError) + if (result && error == QFile::NoError) { status = NoError; - else { + } else { if (error == QFile::ReadError) status = FileReadError; else if (error == QFile::OpenError) @@ -1119,9 +1119,8 @@ void QZipReader::close() QZipWriter::QZipWriter(const QString &fileName, QIODevice::OpenMode mode) { QScopedPointer<QFile> f(new QFile(fileName)); - f->open(mode); QZipWriter::Status status; - if (f->error() == QFile::NoError) + if (f->open(mode) && f->error() == QFile::NoError) status = QZipWriter::NoError; else { if (f->error() == QFile::WriteError) diff --git a/src/gui/util/qgridlayoutengine_p.h b/src/gui/util/qgridlayoutengine_p.h index b3ed2d287b..d79d4cc9c9 100644 --- a/src/gui/util/qgridlayoutengine_p.h +++ b/src/gui/util/qgridlayoutengine_p.h @@ -210,6 +210,7 @@ public: return *t; } }; +Q_DECLARE_TYPEINFO(QGridLayoutBox, Q_MOVABLE_TYPE); // cannot be Q_PRIMITIVE_TYPE, as q_maximumSize, say, is != 0 bool operator==(const QGridLayoutBox &box1, const QGridLayoutBox &box2); inline bool operator!=(const QGridLayoutBox &box1, const QGridLayoutBox &box2) |