From de1b1cd7a6a15755bd5f049533791554c933686c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trond=20Kjern=C3=A5sen?= Date: Thu, 9 Sep 2010 16:51:18 +0200 Subject: Added basic tests for threaded QPainter drawing onto FBOs and PBOs. I've noticed that under Windows we get some wglMakeCurrent() failed messages in the QGLWidget, QGLPixelBuffer and QGLFrameBufferObject tests when deleting the painter or device wrappers used in the threading tests. It's currently unknown why this happens, as both the contexts and devices are valid at that point. --- tests/auto/qglthreads/tst_qglthreads.cpp | 160 +++++++++++++++++++++++++++---- tests/auto/qglthreads/tst_qglthreads.h | 2 + 2 files changed, 141 insertions(+), 21 deletions(-) diff --git a/tests/auto/qglthreads/tst_qglthreads.cpp b/tests/auto/qglthreads/tst_qglthreads.cpp index 4777fb1..38b0dab 100644 --- a/tests/auto/qglthreads/tst_qglthreads.cpp +++ b/tests/auto/qglthreads/tst_qglthreads.cpp @@ -456,11 +456,71 @@ void tst_QGLThreads::renderInThread() QVERIFY(!thread.failure); } +class Device +{ +public: + virtual ~Device() {} + virtual QPaintDevice *realPaintDevice() = 0; + virtual void prepareDevice() {} +}; + +class GLWidgetWrapper : public Device +{ +public: + GLWidgetWrapper() { + widget.resize(150, 150); + widget.show(); + QTest::qWaitForWindowShown(&widget); + widget.doneCurrent(); + } + QPaintDevice *realPaintDevice() { return &widget; } + + ThreadSafeGLWidget widget; +}; + +class PixmapWrapper : public Device +{ +public: + PixmapWrapper() { pixmap = new QPixmap(512, 512); } + ~PixmapWrapper() { delete pixmap; } + QPaintDevice *realPaintDevice() { return pixmap; } + + QPixmap *pixmap; +}; + +class PixelBufferWrapper : public Device +{ +public: + PixelBufferWrapper() { pbuffer = new QGLPixelBuffer(512, 512); } + ~PixelBufferWrapper() { delete pbuffer; } + QPaintDevice *realPaintDevice() { return pbuffer; } + + QGLPixelBuffer *pbuffer; +}; + + +class FrameBufferObjectWrapper : public Device +{ +public: + FrameBufferObjectWrapper() { + widget.makeCurrent(); + fbo = new QGLFramebufferObject(512, 512); + widget.doneCurrent(); + } + ~FrameBufferObjectWrapper() { delete fbo; } + QPaintDevice *realPaintDevice() { return fbo; } + void prepareDevice() { widget.makeCurrent(); } + + ThreadSafeGLWidget widget; + QGLFramebufferObject *fbo; +}; + + class ThreadPainter : public QObject { Q_OBJECT public: - ThreadPainter(QPaintDevice *pd) : paintDevice(pd), fail(true) { + ThreadPainter(Device *pd) : device(pd), fail(true) { pixmap = QPixmap(40, 40); pixmap.fill(Qt::green); QPainter p(&pixmap); @@ -474,6 +534,8 @@ public slots: QTime time; time.start(); int rotAngle = 10; + device->prepareDevice(); + QPaintDevice *paintDevice = device->realPaintDevice(); QSize s(paintDevice->width(), paintDevice->height()); while (time.elapsed() < RUNNING_TIME) { QPainter p; @@ -506,7 +568,7 @@ public slots: private: QPixmap pixmap; - QPaintDevice *paintDevice; + Device *device; bool fail; }; @@ -520,16 +582,6 @@ public: devices.append(new T); threads.append(new QThread); painters.append(new ThreadPainter(devices.at(i))); - if (devices.at(i)->devType() == QInternal::Widget) { - QWidget *widget = static_cast(devices.at(i)); - widget->resize(150, 150); - widget->show(); - QTest::qWaitForWindowShown(widget); - if (widget->inherits("QGLWidget")) { - QGLWidget *glWidget = static_cast(widget); - glWidget->doneCurrent(); - } - } painters.at(i)->moveToThread(threads.at(i)); painters.at(i)->connect(threads.at(i), SIGNAL(started()), painters.at(i), SLOT(draw())); } @@ -568,7 +620,7 @@ public: private: QList threads; - QList devices; + QList devices; QList painters; int numThreads; }; @@ -587,7 +639,12 @@ void tst_QGLThreads::painterOnGLWidgetInThread() #ifdef Q_OS_MAC QSKIP("OpenGL threading tests are currently disabled on Mac as they were causing reboots", SkipAll); #endif - PaintThreadManager painterThreads(5); + if (!((QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_0) || + (QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_Version_2_0))) { + QSKIP("The OpenGL based threaded QPainter tests requires OpenGL/ES 2.0.", SkipAll); + } + + PaintThreadManager painterThreads(5); painterThreads.start(); while (painterThreads.areRunning()) { @@ -601,12 +658,6 @@ void tst_QGLThreads::painterOnGLWidgetInThread() QVERIFY(!painterThreads.failed()); } -class Pixmap : public QPixmap -{ -public: - Pixmap() : QPixmap(200, 200) {} -}; - /* This test uses QPainter to draw onto different QPixmaps in different threads at the same time. @@ -616,7 +667,74 @@ void tst_QGLThreads::painterOnPixmapInThread() #ifdef Q_WS_X11 QSKIP("Drawing text in threads onto X11 drawables currently crashes on some X11 servers.", SkipAll); #endif - PaintThreadManager painterThreads(5); + PaintThreadManager painterThreads(5); + painterThreads.start(); + + while (painterThreads.areRunning()) { + qApp->processEvents(); +#ifdef Q_WS_WIN + Sleep(100); +#else + usleep(100 * 1000); +#endif + } + QVERIFY(!painterThreads.failed()); +} + +/* This test uses QPainter to draw onto different QGLPixelBuffer + objects in different threads at the same time. +*/ +void tst_QGLThreads::painterOnPboInThread() +{ +#ifdef Q_OS_MAC + QSKIP("OpenGL threading tests are currently disabled on Mac as they were causing reboots", SkipAll); +#endif + if (!((QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_0) || + (QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_Version_2_0))) { + QSKIP("The OpenGL based threaded QPainter tests requires OpenGL/ES 2.0.", SkipAll); + return; + } + + if (!QGLPixelBuffer::hasOpenGLPbuffers()) { + QSKIP("This system doesn't support pbuffers.", SkipAll); + return; + } + + PaintThreadManager painterThreads(5); + painterThreads.start(); + + while (painterThreads.areRunning()) { + qApp->processEvents(); +#ifdef Q_WS_WIN + Sleep(100); +#else + usleep(100 * 1000); +#endif + } + QVERIFY(!painterThreads.failed()); +} + +/* This test uses QPainter to draw onto different + QGLFramebufferObjects (bound in a QGLWidget's context) in different + threads at the same time. +*/ +void tst_QGLThreads::painterOnFboInThread() +{ +#ifdef Q_OS_MAC + QSKIP("OpenGL threading tests are currently disabled on Mac as they were causing reboots", SkipAll); +#endif + if (!((QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_0) || + (QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_Version_2_0))) { + QSKIP("The OpenGL based threaded QPainter tests requires OpenGL/ES 2.0.", SkipAll); + return; + } + + if (!QGLFramebufferObject::hasOpenGLFramebufferObjects()) { + QSKIP("This system doesn't support framebuffer objects.", SkipAll); + return; + } + + PaintThreadManager painterThreads(5); painterThreads.start(); while (painterThreads.areRunning()) { diff --git a/tests/auto/qglthreads/tst_qglthreads.h b/tests/auto/qglthreads/tst_qglthreads.h index ae6b953..a8c2963 100644 --- a/tests/auto/qglthreads/tst_qglthreads.h +++ b/tests/auto/qglthreads/tst_qglthreads.h @@ -58,6 +58,8 @@ private slots: void renderInThread(); void painterOnGLWidgetInThread(); void painterOnPixmapInThread(); + void painterOnPboInThread(); + void painterOnFboInThread(); }; #endif // TST_QGLTHREADS_H -- cgit v0.12 From b7ef9d49a969f512ecbc7ad940642f3a74e348ad Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Tue, 7 Sep 2010 13:57:07 +0200 Subject: Support sub pixel positioning of glyphs in raster engine For the raster engine to provide the same quality of text rendering as the native engine on Mac Cocoa, we need to support rendering to different sub pixel positions for each glyph. The number of subpixel positions is arbitrary and has to be detected, but it's usually three or four. Each position will give slightly different coverages inside the pixel and thus different rasterizations. Other font engines which support sub pixel positioning of glyphs can provide the same functionality by implementing supportsSubPixelPositions() to return true, and then adding the subPixelPosition argument to the x coordinate used in alphaRGBMapForGlyph(). Task-number: QTBUG-5053 Reviewed-by: Jiang Jiang --- src/gui/painting/qpaintengine_raster.cpp | 10 ++- src/gui/painting/qtextureglyphcache.cpp | 97 +++++++++++++++++++--- src/gui/painting/qtextureglyphcache_p.h | 36 ++++++-- src/gui/text/qfontengine.cpp | 2 +- src/gui/text/qfontengine_ft.cpp | 6 +- src/gui/text/qfontengine_ft_p.h | 2 +- src/gui/text/qfontengine_mac.mm | 15 ++-- src/gui/text/qfontengine_p.h | 10 ++- src/gui/text/qfontengine_win.cpp | 2 +- src/gui/text/qfontengine_win_p.h | 2 +- .../gl2paintengineex/qpaintengineex_opengl2.cpp | 9 +- .../gl2paintengineex/qtextureglyphcache_gl.cpp | 6 +- .../gl2paintengineex/qtextureglyphcache_gl_p.h | 2 +- tests/auto/qpainter/tst_qpainter.cpp | 36 ++++++++ 14 files changed, 191 insertions(+), 44 deletions(-) diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp index 0b76898..94d7578 100644 --- a/src/gui/painting/qpaintengine_raster.cpp +++ b/src/gui/painting/qpaintengine_raster.cpp @@ -3098,9 +3098,17 @@ void QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs, int margin = cache->glyphMargin(); + bool supportsSubPixelPositions = fontEngine->supportsSubPixelPositions(); + const uchar *bits = image.bits(); for (int i=0; icoords.value(glyphs[i]); + + QFixed subPixelPosition; + if (supportsSubPixelPositions) + subPixelPosition = cache->subPixelPositionForX(positions[i].x); + QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphs[i], subPixelPosition); + const QTextureGlyphCache::Coord &c = cache->coords.value(glyph); + int x = qFloor(positions[i].x) + c.baseLineX - margin; int y = qFloor(positions[i].y) - c.baseLineY - margin; diff --git a/src/gui/painting/qtextureglyphcache.cpp b/src/gui/painting/qtextureglyphcache.cpp index 376219b..e992bb2 100644 --- a/src/gui/painting/qtextureglyphcache.cpp +++ b/src/gui/painting/qtextureglyphcache.cpp @@ -69,8 +69,60 @@ static inline int qt_next_power_of_two(int v) return v; } +int QTextureGlyphCache::calculateSubPixelPositionCount(glyph_t glyph) const +{ + // Test 12 different subpixel positions since it factors into 3*4 so it gives + // the coverage we need. + + QList images; + for (int i=0; i<12; ++i) { + QImage img = textureMapForGlyph(glyph, QFixed::fromReal(i / 12.0)); + + if (images.isEmpty()) { + QPainterPath path; + QFixedPoint point; + m_current_fontengine->addGlyphsToPath(&glyph, &point, 1, &path, QTextItem::RenderFlags()); + + // Glyph is space, return 0 to indicate that we need to keep trying + if (path.isEmpty()) + break; + + images.append(img); + } else { + bool found = false; + for (int j=0; j listItemCoordinates; + bool supportsSubPixelPositions = fontEngine->supportsSubPixelPositions(); + if (m_subPixelPositionCount == 0) { + if (!supportsSubPixelPositions) { + m_subPixelPositionCount = 1; + } else { + int i = 0; + while (m_subPixelPositionCount == 0 && i < numGlyphs) + m_subPixelPositionCount = calculateSubPixelPositionCount(glyphs[i++]); + } + } + + QHash listItemCoordinates; int rowHeight = 0; // check each glyph for its metrics and get the required rowHeight. for (int i=0; i < numGlyphs; ++i) { const glyph_t glyph = glyphs[i]; - if (coords.contains(glyph)) + + QFixed subPixelPosition; + if (supportsSubPixelPositions) { + QFixed x = positions != 0 ? positions[i].x : QFixed(); + subPixelPosition = subPixelPositionForX(x); + } + + if (coords.contains(GlyphAndSubPixelPosition(glyph, subPixelPosition))) continue; - if (listItemCoordinates.contains(glyph)) + if (listItemCoordinates.contains(GlyphAndSubPixelPosition(glyph, subPixelPosition))) continue; glyph_metrics_t metrics = fontEngine->boundingBox(glyph, m_transform); @@ -119,7 +189,7 @@ void QTextureGlyphCache::populate(QFontEngine *fontEngine, int numGlyphs, const metrics.x.round().truncate(), -metrics.y.truncate() }; // baseline for horizontal scripts - listItemCoordinates.insert(glyph, c); + listItemCoordinates.insert(GlyphAndSubPixelPosition(glyph, subPixelPosition), c); rowHeight = qMax(rowHeight, glyph_height); } if (listItemCoordinates.isEmpty()) @@ -135,7 +205,7 @@ void QTextureGlyphCache::populate(QFontEngine *fontEngine, int numGlyphs, const } // now actually use the coords and paint the wanted glyps into cache. - QHash::iterator iter = listItemCoordinates.begin(); + QHash::iterator iter = listItemCoordinates.begin(); while (iter != listItemCoordinates.end()) { Coord c = iter.value(); @@ -166,7 +236,7 @@ void QTextureGlyphCache::fillInPendingGlyphs() int requiredHeight = 0; { - QHash::iterator iter = m_pendingGlyphs.begin(); + QHash::iterator iter = m_pendingGlyphs.begin(); while (iter != m_pendingGlyphs.end()) { Coord c = iter.value(); requiredHeight = qMax(requiredHeight, c.y + c.h); @@ -182,9 +252,10 @@ void QTextureGlyphCache::fillInPendingGlyphs() } { - QHash::iterator iter = m_pendingGlyphs.begin(); + QHash::iterator iter = m_pendingGlyphs.begin(); while (iter != m_pendingGlyphs.end()) { - fillTexture(iter.value(), iter.key()); + GlyphAndSubPixelPosition key = iter.key(); + fillTexture(iter.value(), key.glyph, key.subPixelPosition); ++iter; } @@ -193,7 +264,7 @@ void QTextureGlyphCache::fillInPendingGlyphs() m_pendingGlyphs.clear(); } -QImage QTextureGlyphCache::textureMapForGlyph(glyph_t g) const +QImage QTextureGlyphCache::textureMapForGlyph(glyph_t g, QFixed subPixelPosition) const { #if defined(Q_WS_X11) if (m_transform.type() > QTransform::TxTranslate) { @@ -226,7 +297,7 @@ QImage QTextureGlyphCache::textureMapForGlyph(glyph_t g) const } else #endif if (m_type == QFontEngineGlyphCache::Raster_RGBMask) - return m_current_fontengine->alphaRGBMapForGlyph(g, glyphMargin(), m_transform); + return m_current_fontengine->alphaRGBMapForGlyph(g, subPixelPosition, glyphMargin(), m_transform); else return m_current_fontengine->alphaMapForGlyph(g, m_transform); @@ -272,9 +343,9 @@ int QImageTextureGlyphCache::glyphMargin() const #endif } -void QImageTextureGlyphCache::fillTexture(const Coord &c, glyph_t g) +void QImageTextureGlyphCache::fillTexture(const Coord &c, glyph_t g, QFixed subPixelPosition) { - QImage mask = textureMapForGlyph(g); + QImage mask = textureMapForGlyph(g, subPixelPosition); #ifdef CACHE_DEBUG printf("fillTexture of %dx%d at %d,%d in the cache of %dx%d\n", c.w, c.h, c.x, c.y, m_image.width(), m_image.height()); diff --git a/src/gui/painting/qtextureglyphcache_p.h b/src/gui/painting/qtextureglyphcache_p.h index 0770ed4..4131003 100644 --- a/src/gui/painting/qtextureglyphcache_p.h +++ b/src/gui/painting/qtextureglyphcache_p.h @@ -77,11 +77,24 @@ class Q_GUI_EXPORT QTextureGlyphCache : public QFontEngineGlyphCache public: QTextureGlyphCache(QFontEngineGlyphCache::Type type, const QTransform &matrix) : QFontEngineGlyphCache(matrix, type), m_current_fontengine(0), - m_w(0), m_h(0), m_cx(0), m_cy(0), m_currentRowHeight(0) + m_w(0), m_h(0), m_cx(0), m_cy(0), m_currentRowHeight(0), m_subPixelPositionCount(0) { } virtual ~QTextureGlyphCache() { } + 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 Coord { int x; int y; @@ -101,7 +114,7 @@ public: virtual int glyphMargin() const { return 0; } virtual int glyphPadding() const { return 0; } - virtual void fillTexture(const Coord &coord, glyph_t glyph) = 0; + virtual void fillTexture(const Coord &coord, glyph_t glyph, QFixed subPixelPosition) = 0; inline void createCache(int width, int height) { m_w = width; @@ -118,22 +131,31 @@ public: inline bool isNull() const { return m_h == 0; } - QHash coords; + QHash coords; - QImage textureMapForGlyph(glyph_t g) const; + QImage textureMapForGlyph(glyph_t g, QFixed subPixelPosition) const; + + QFixed subPixelPositionForX(QFixed x) const; protected: - QFontEngine *m_current_fontengine; + int calculateSubPixelPositionCount(glyph_t) const; - QHash m_pendingGlyphs; + QFontEngine *m_current_fontengine; + QHash m_pendingGlyphs; int m_w; // image width int m_h; // image height int m_cx; // current x int m_cy; // current y int m_currentRowHeight; // Height of last row + int m_subPixelPositionCount; // Number of positions within a single pixel for this cache }; +inline uint qHash(const QTextureGlyphCache::GlyphAndSubPixelPosition &g) +{ + return (g.glyph << 8) | (g.subPixelPosition * 10).round().toInt(); +} + class Q_GUI_EXPORT QImageTextureGlyphCache : public QTextureGlyphCache { @@ -143,7 +165,7 @@ public: virtual int glyphMargin() const; virtual void createTextureData(int width, int height); virtual void resizeTextureData(int width, int height); - virtual void fillTexture(const Coord &c, glyph_t glyph); + virtual void fillTexture(const Coord &c, glyph_t glyph, QFixed subPixelPosition); inline const QImage &image() const { return m_image; } diff --git a/src/gui/text/qfontengine.cpp b/src/gui/text/qfontengine.cpp index 1b94915..55d2a83 100644 --- a/src/gui/text/qfontengine.cpp +++ b/src/gui/text/qfontengine.cpp @@ -616,7 +616,7 @@ QImage QFontEngine::alphaMapForGlyph(glyph_t glyph, const QTransform &t) return i; } -QImage QFontEngine::alphaRGBMapForGlyph(glyph_t glyph, int /* margin */, const QTransform &t) +QImage QFontEngine::alphaRGBMapForGlyph(glyph_t glyph, QFixed /*subPixelPosition*/, int /* margin */, const QTransform &t) { QImage alphaMask = alphaMapForGlyph(glyph, t); QImage rgbMask(alphaMask.width(), alphaMask.height(), QImage::Format_RGB32); diff --git a/src/gui/text/qfontengine_ft.cpp b/src/gui/text/qfontengine_ft.cpp index 7a8b1e5..7b6ef0e 100644 --- a/src/gui/text/qfontengine_ft.cpp +++ b/src/gui/text/qfontengine_ft.cpp @@ -1873,10 +1873,10 @@ QImage QFontEngineFT::alphaMapForGlyph(glyph_t g) return img; } -QImage QFontEngineFT::alphaRGBMapForGlyph(glyph_t g, int margin, const QTransform &t) +QImage QFontEngineFT::alphaRGBMapForGlyph(glyph_t g, QFixed subPixelPosition, int margin, const QTransform &t) { if (t.type() > QTransform::TxTranslate) - return QFontEngine::alphaRGBMapForGlyph(g, margin, t); + return QFontEngine::alphaRGBMapForGlyph(g, subPixelPosition, margin, t); lockFace(); @@ -1885,7 +1885,7 @@ QImage QFontEngineFT::alphaRGBMapForGlyph(glyph_t g, int margin, const QTransfor Glyph *glyph = defaultGlyphSet.outline_drawing ? 0 : loadGlyph(g, glyph_format); if (!glyph) { unlockFace(); - return QFontEngine::alphaRGBMapForGlyph(g, margin, t); + return QFontEngine::alphaRGBMapForGlyph(g, subPixelPosition, margin, t); } QImage img(glyph->width, glyph->height, QImage::Format_RGB32); diff --git a/src/gui/text/qfontengine_ft_p.h b/src/gui/text/qfontengine_ft_p.h index 2f05a8b..72f7d9f 100644 --- a/src/gui/text/qfontengine_ft_p.h +++ b/src/gui/text/qfontengine_ft_p.h @@ -241,7 +241,7 @@ private: virtual void recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const; virtual QImage alphaMapForGlyph(glyph_t); - virtual QImage alphaRGBMapForGlyph(glyph_t, int margin, const QTransform &t); + virtual QImage alphaRGBMapForGlyph(glyph_t, QFixed subPixelPosition, int margin, const QTransform &t); virtual void removeGlyphFromCache(glyph_t glyph); virtual int glyphCount() const; diff --git a/src/gui/text/qfontengine_mac.mm b/src/gui/text/qfontengine_mac.mm index 2bbf9f2..1ca6ec3 100644 --- a/src/gui/text/qfontengine_mac.mm +++ b/src/gui/text/qfontengine_mac.mm @@ -663,7 +663,7 @@ QFont QCoreTextFontEngine::createExplicitFont() const return createExplicitFontWithName(familyName); } -QImage QCoreTextFontEngine::imageForGlyph(glyph_t glyph, int margin, bool aa) +QImage QCoreTextFontEngine::imageForGlyph(glyph_t glyph, QFixed subPixelPosition, int /*margin*/, bool aa) { const glyph_metrics_t br = boundingBox(glyph); QImage im(qRound(br.width)+2, qRound(br.height)+2, QImage::Format_RGB32); @@ -700,7 +700,8 @@ QImage QCoreTextFontEngine::imageForGlyph(glyph_t glyph, int margin, bool aa) QCFType cgFont = CGFontCreateWithPlatformFont(&atsfont); CGContextSetFont(ctx, cgFont); - qreal pos_x = -br.x.toReal()+1, pos_y = im.height()+br.y.toReal(); + qreal pos_x = -br.x.toReal() + subPixelPosition.toReal(); + qreal pos_y = im.height()+br.y.toReal(); CGContextSetTextPosition(ctx, pos_x, pos_y); CGSize advance; @@ -721,7 +722,7 @@ QImage QCoreTextFontEngine::imageForGlyph(glyph_t glyph, int margin, bool aa) QImage QCoreTextFontEngine::alphaMapForGlyph(glyph_t glyph) { - QImage im = imageForGlyph(glyph, 0, false); + QImage im = imageForGlyph(glyph, QFixed(), 0, false); QImage indexed(im.width(), im.height(), QImage::Format_Indexed8); QVector colors(256); @@ -742,12 +743,12 @@ QImage QCoreTextFontEngine::alphaMapForGlyph(glyph_t glyph) return indexed; } -QImage QCoreTextFontEngine::alphaRGBMapForGlyph(glyph_t glyph, int margin, const QTransform &x) +QImage QCoreTextFontEngine::alphaRGBMapForGlyph(glyph_t glyph, QFixed subPixelPosition, int margin, const QTransform &x) { if (x.type() >= QTransform::TxScale) - return QFontEngine::alphaRGBMapForGlyph(glyph, margin, x); + return QFontEngine::alphaRGBMapForGlyph(glyph, subPixelPosition, margin, x); - QImage im = imageForGlyph(glyph, margin, true); + QImage im = imageForGlyph(glyph, subPixelPosition, margin, true); qmacfontengine_gamma_correct(&im); return im; } @@ -1706,7 +1707,7 @@ QImage QFontEngineMac::alphaMapForGlyph(glyph_t glyph) return indexed; } -QImage QFontEngineMac::alphaRGBMapForGlyph(glyph_t glyph, int margin, const QTransform &t) +QImage QFontEngineMac::alphaRGBMapForGlyph(glyph_t glyph, QFixed, int margin, const QTransform &t) { QImage im = imageForGlyph(glyph, margin, true); diff --git a/src/gui/text/qfontengine_p.h b/src/gui/text/qfontengine_p.h index 6e9bd0bc..6ebbc1f 100644 --- a/src/gui/text/qfontengine_p.h +++ b/src/gui/text/qfontengine_p.h @@ -155,6 +155,7 @@ public: SynthesizedStretch = 0x4 }; virtual int synthesized() const { return 0; } + virtual bool supportsSubPixelPositions() const { return false; } virtual QFixed emSquareSize() const { return ascent(); } @@ -188,7 +189,7 @@ public: */ virtual QImage alphaMapForGlyph(glyph_t); virtual QImage alphaMapForGlyph(glyph_t, const QTransform &t); - virtual QImage alphaRGBMapForGlyph(glyph_t, int margin, const QTransform &t); + virtual QImage alphaRGBMapForGlyph(glyph_t, QFixed subPixelPosition, int margin, const QTransform &t); virtual void removeGlyphFromCache(glyph_t); @@ -448,6 +449,7 @@ public: virtual bool canRender(const QChar *string, int len); virtual int synthesized() const { return synthesisFlags; } + virtual bool supportsSubPixelPositions() const { return true; } virtual Type type() const { return QFontEngine::Mac; } @@ -457,13 +459,13 @@ public: virtual bool getSfntTableData(uint /*tag*/, uchar * /*buffer*/, uint * /*length*/) const; virtual void getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics); virtual QImage alphaMapForGlyph(glyph_t); - virtual QImage alphaRGBMapForGlyph(glyph_t, int margin, const QTransform &t); + virtual QImage alphaRGBMapForGlyph(glyph_t, QFixed subPixelPosition, int margin, const QTransform &t); virtual qreal minRightBearing() const; virtual qreal minLeftBearing() const; virtual QFont createExplicitFont() const; private: - QImage imageForGlyph(glyph_t glyph, int margin, bool colorful); + QImage imageForGlyph(glyph_t glyph, QFixed subPixelPosition, int margin, bool colorful); CTFontRef ctfont; CGFontRef cgFont; QCoreTextFontEngineMulti *parentEngine; @@ -546,7 +548,7 @@ public: virtual Properties properties() const; virtual void getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics); virtual QImage alphaMapForGlyph(glyph_t); - virtual QImage alphaRGBMapForGlyph(glyph_t, int margin, const QTransform &t); + virtual QImage alphaRGBMapForGlyph(glyph_t, QFixed subPixelPosition, int margin, const QTransform &t); private: QImage imageForGlyph(glyph_t glyph, int margin, bool colorful); diff --git a/src/gui/text/qfontengine_win.cpp b/src/gui/text/qfontengine_win.cpp index 4bed2b5..928bca0 100644 --- a/src/gui/text/qfontengine_win.cpp +++ b/src/gui/text/qfontengine_win.cpp @@ -1254,7 +1254,7 @@ QImage QFontEngineWin::alphaMapForGlyph(glyph_t glyph, const QTransform &xform) #define SPI_GETFONTSMOOTHINGCONTRAST 0x200C #define SPI_SETFONTSMOOTHINGCONTRAST 0x200D -QImage QFontEngineWin::alphaRGBMapForGlyph(glyph_t glyph, int margin, const QTransform &t) +QImage QFontEngineWin::alphaRGBMapForGlyph(glyph_t glyph, QFixed, int margin, const QTransform &t) { HFONT font = hfont; diff --git a/src/gui/text/qfontengine_win_p.h b/src/gui/text/qfontengine_win_p.h index 68b53b5..d86f42e 100644 --- a/src/gui/text/qfontengine_win_p.h +++ b/src/gui/text/qfontengine_win_p.h @@ -104,7 +104,7 @@ public: virtual QImage alphaMapForGlyph(glyph_t t) { return alphaMapForGlyph(t, QTransform()); } virtual QImage alphaMapForGlyph(glyph_t, const QTransform &xform); - virtual QImage alphaRGBMapForGlyph(glyph_t t, int margin, const QTransform &xform); + virtual QImage alphaRGBMapForGlyph(glyph_t t, QFixed subPixelPosition, int margin, const QTransform &xform); #ifndef Q_CC_MINGW virtual void getGlyphBearings(glyph_t glyph, qreal *leftBearing = 0, qreal *rightBearing = 0); diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp index 6e56553..e7ee91c 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp @@ -1519,8 +1519,15 @@ void QGL2PaintEngineExPrivate::drawCachedGlyphs(QFontEngineGlyphCache::Type glyp vertexCoordinates->clear(); textureCoordinates->clear(); + bool supportsSubPixelPositions = staticTextItem->fontEngine->supportsSubPixelPositions(); for (int i=0; inumGlyphs; ++i) { - const QTextureGlyphCache::Coord &c = cache->coords.value(staticTextItem->glyphs[i]); + QFixed subPixelPosition; + if (supportsSubPixelPositions) + subPixelPosition = cache->subPixelPositionForX(staticTextItem->glyphPositions[i].x); + + QTextureGlyphCache::GlyphAndSubPixelPosition glyph(staticTextItem->glyphs[i], subPixelPosition); + + const QTextureGlyphCache::Coord &c = cache->coords.value(glyph); int x = staticTextItem->glyphPositions[i].x.toInt() + c.baseLineX - margin; int y = staticTextItem->glyphPositions[i].y.toInt() - c.baseLineY - margin; diff --git a/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp b/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp index 53a2f3a..3dc7789 100644 --- a/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp +++ b/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp @@ -216,7 +216,7 @@ void QGLTextureGlyphCache::resizeTextureData(int width, int height) pex->updateClipScissorTest(); } -void QGLTextureGlyphCache::fillTexture(const Coord &c, glyph_t glyph) +void QGLTextureGlyphCache::fillTexture(const Coord &c, glyph_t glyph, QFixed subPixelPosition) { if (ctx == 0) { qWarning("QGLTextureGlyphCache::fillTexture: Called with no context"); @@ -225,7 +225,7 @@ void QGLTextureGlyphCache::fillTexture(const Coord &c, glyph_t glyph) QGLGlyphTexture *glyphTexture = m_textureResource.value(ctx); if (pex == 0 || ctx->d_ptr->workaround_brokenFBOReadBack) { - QImageTextureGlyphCache::fillTexture(c, glyph); + QImageTextureGlyphCache::fillTexture(c, glyph, subPixelPosition); glBindTexture(GL_TEXTURE_2D, glyphTexture->m_texture); const QImage &texture = image(); @@ -238,7 +238,7 @@ void QGLTextureGlyphCache::fillTexture(const Coord &c, glyph_t glyph) return; } - QImage mask = textureMapForGlyph(glyph); + QImage mask = textureMapForGlyph(glyph, subPixelPosition); const int maskWidth = mask.width(); const int maskHeight = mask.height(); diff --git a/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h b/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h index 2ae3a64..345684d 100644 --- a/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h +++ b/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h @@ -106,7 +106,7 @@ public: virtual void createTextureData(int width, int height); virtual void resizeTextureData(int width, int height); - virtual void fillTexture(const Coord &c, glyph_t glyph); + virtual void fillTexture(const Coord &c, glyph_t glyph, QFixed subPixelPosition); virtual int glyphPadding() const; inline GLuint texture() const { diff --git a/tests/auto/qpainter/tst_qpainter.cpp b/tests/auto/qpainter/tst_qpainter.cpp index 1ba5859..06de16a 100644 --- a/tests/auto/qpainter/tst_qpainter.cpp +++ b/tests/auto/qpainter/tst_qpainter.cpp @@ -221,6 +221,8 @@ private slots: void drawRect_task215378(); void drawRect_task247505(); + void drawText_subPixelPositionsInRaster_qtbug5053(); + void drawImage_data(); void drawImage(); @@ -4562,6 +4564,40 @@ void tst_QPainter::clipBoundingRect() } +void tst_QPainter::drawText_subPixelPositionsInRaster_qtbug5053() +{ +#if !defined(Q_WS_MAC) + QSKIP("Only mac supports sub pixel positions currently", SkipAll); +#endif + + QFontMetricsF fm(qApp->font()); + + QImage baseLine(fm.width(QChar::fromLatin1('e')), fm.height(), QImage::Format_RGB32); + baseLine.fill(Qt::white); + { + QPainter p(&baseLine); + p.drawText(0, fm.ascent(), QString::fromLatin1("e")); + } + + bool foundDifferentRasterization = false; + for (int i=1; i<12; ++i) { + QImage comparison(baseLine.size(), QImage::Format_RGB32); + comparison.fill(Qt::white); + + { + QPainter p(&comparison); + p.drawText(QPointF(i / 12.0, fm.ascent()), QString::fromLatin1("e")); + } + + if (comparison != baseLine) { + foundDifferentRasterization = true; + break; + } + } + + QVERIFY(foundDifferentRasterization); +} + QTEST_MAIN(tst_QPainter) #include "tst_qpainter.moc" -- cgit v0.12