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 From f2f6330d915cbe3d0989ad280738ed0a6954cd35 Mon Sep 17 00:00:00 2001 From: Jiang Jiang Date: Fri, 3 Sep 2010 15:49:33 +0200 Subject: Fix Core Text font loading for certain Mac Fonts Font names enumerated by Core Text cannot be found by ATSFontFamilyFindFromName because ATS is expecting native names instead of the names returned by Core Text. This patch get rid of ATS font matching code in Cocoa code path to simplify the code, avoid deprecation warnings (in the future) and fix this issue. Task-number: QTBUG-11145 Reviewed-by: Eskil --- src/gui/text/qfontdatabase_mac.cpp | 45 +++++++++++++------------- src/gui/text/qfontengine_mac.mm | 5 +-- src/gui/text/qfontengine_p.h | 3 +- tests/auto/qfontdatabase/tst_qfontdatabase.cpp | 35 +++++++++++++++++++- tests/auto/qglyphs/tst_qglyphs.cpp | 8 ----- 5 files changed, 58 insertions(+), 38 deletions(-) diff --git a/src/gui/text/qfontdatabase_mac.cpp b/src/gui/text/qfontdatabase_mac.cpp index 4648304..5c41e0a 100644 --- a/src/gui/text/qfontdatabase_mac.cpp +++ b/src/gui/text/qfontdatabase_mac.cpp @@ -293,8 +293,12 @@ void QFontDatabase::load(const QFontPrivate *d, int script) // previous versions family_list << QApplication::font().defaultFamily(); +#if defined(QT_MAC_USE_COCOA) + QCFString fontName = NULL, familyName = NULL; +#else ATSFontFamilyRef familyRef = 0; ATSFontRef fontRef = 0; +#endif QMutexLocker locker(fontDatabaseMutex()); QFontDatabasePrivate *db = privateDb(); @@ -304,26 +308,21 @@ void QFontDatabase::load(const QFontPrivate *d, int script) for (int k = 0; k < db->count; ++k) { if (db->families[k]->name.compare(family_list.at(i), Qt::CaseInsensitive) == 0) { QByteArray family_name = db->families[k]->name.toUtf8(); +#if defined(QT_MAC_USE_COCOA) + CTFontRef ctFont = CTFontCreateWithName(QCFString(db->families[k]->name), 12, NULL); + if (ctFont) { + fontName = CTFontCopyFullName(ctFont); + familyName = CTFontCopyFamilyName(ctFont); + CFRelease(ctFont); + goto FamilyFound; + } +#else familyRef = ATSFontFamilyFindFromName(QCFString(db->families[k]->name), kATSOptionFlagsDefault); if (familyRef) { fontRef = ATSFontFindFromName(QCFString(db->families[k]->name), kATSOptionFlagsDefault); goto FamilyFound; - } else { -#if defined(QT_MAC_USE_COCOA) - // ATS and CT disagrees on what the family name should be, - // use CT to look up the font if ATS fails. - QCFString familyName = QString::fromAscii(family_name); - QCFType CTfontRef = CTFontCreateWithName(familyName, 12, NULL); - QCFType fontDescriptor = CTFontCopyFontDescriptor(CTfontRef); - QCFString displayName = (CFStringRef)CTFontDescriptorCopyAttribute(fontDescriptor, kCTFontDisplayNameAttribute); - - familyRef = ATSFontFamilyFindFromName(displayName, kATSOptionFlagsDefault); - if (familyRef) { - fontRef = ATSFontFindFromName(displayName, kATSOptionFlagsDefault); - goto FamilyFound; - } -#endif } +#endif } } } @@ -331,18 +330,18 @@ FamilyFound: //fill in the engine's font definition QFontDef fontDef = d->request; //copy.. if(fontDef.pointSize < 0) - fontDef.pointSize = qt_mac_pointsize(fontDef, d->dpi); + fontDef.pointSize = qt_mac_pointsize(fontDef, d->dpi); else - fontDef.pixelSize = qt_mac_pixelsize(fontDef, d->dpi); - { - QCFString actualName; - if(ATSFontFamilyGetName(familyRef, kATSOptionFlagsDefault, &actualName) == noErr) - fontDef.family = actualName; - } + fontDef.pixelSize = qt_mac_pixelsize(fontDef, d->dpi); #ifdef QT_MAC_USE_COCOA - QFontEngine *engine = new QCoreTextFontEngineMulti(familyRef, fontRef, fontDef, d->kerning); + fontDef.family = familyName; + QFontEngine *engine = new QCoreTextFontEngineMulti(fontName, fontDef, d->kerning); + CFRelease(fontName); #else + QCFString actualName; + if (ATSFontFamilyGetName(familyRef, kATSOptionFlagsDefault, &actualName) == noErr) + fontDef.family = actualName; QFontEngine *engine = new QFontEngineMacMulti(familyRef, fontRef, fontDef, d->kerning); #endif d->engineData->engine = engine; diff --git a/src/gui/text/qfontengine_mac.mm b/src/gui/text/qfontengine_mac.mm index 1ca6ec3..894de44 100644 --- a/src/gui/text/qfontengine_mac.mm +++ b/src/gui/text/qfontengine_mac.mm @@ -143,7 +143,7 @@ void qmacfontengine_gamma_correct(QImage *image) #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 -QCoreTextFontEngineMulti::QCoreTextFontEngineMulti(const ATSFontFamilyRef &, const ATSFontRef &atsFontRef, const QFontDef &fontDef, bool kerning) +QCoreTextFontEngineMulti::QCoreTextFontEngineMulti(const QCFString &name, const QFontDef &fontDef, bool kerning) : QFontEngineMulti(0) { this->fontDef = fontDef; @@ -159,9 +159,6 @@ QCoreTextFontEngineMulti::QCoreTextFontEngineMulti(const ATSFontFamilyRef &, con break; } - QCFString name; - ATSFontGetName(atsFontRef, kATSOptionFlagsDefault, &name); - transform = CGAffineTransformIdentity; if (fontDef.stretch != 100) { transform = CGAffineTransformMakeScale(float(fontDef.stretch) / float(100), 1); diff --git a/src/gui/text/qfontengine_p.h b/src/gui/text/qfontengine_p.h index 6ebbc1f..b3a6c59 100644 --- a/src/gui/text/qfontengine_p.h +++ b/src/gui/text/qfontengine_p.h @@ -477,8 +477,7 @@ private: class QCoreTextFontEngineMulti : public QFontEngineMulti { public: - QCoreTextFontEngineMulti(const ATSFontFamilyRef &atsFamily, const ATSFontRef &atsFontRef, - const QFontDef &fontDef, bool kerning); + QCoreTextFontEngineMulti(const QCFString &name, const QFontDef &fontDef, bool kerning); ~QCoreTextFontEngineMulti(); virtual bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, diff --git a/tests/auto/qfontdatabase/tst_qfontdatabase.cpp b/tests/auto/qfontdatabase/tst_qfontdatabase.cpp index 357b82e..bddd92a 100644 --- a/tests/auto/qfontdatabase/tst_qfontdatabase.cpp +++ b/tests/auto/qfontdatabase/tst_qfontdatabase.cpp @@ -70,6 +70,11 @@ private slots: void fixedPitch_data(); void fixedPitch(); +#ifdef Q_WS_MAC + void trickyFonts_data(); + void trickyFonts(); +#endif + void widthTwoTimes_data(); void widthTwoTimes(); @@ -131,10 +136,16 @@ void tst_QFontDatabase::fixedPitch_data() QTest::newRow( "Times New Roman" ) << QString( "Times New Roman" ) << false; QTest::newRow( "Arial" ) << QString( "Arial" ) << false; - QTest::newRow( "Script" ) << QString( "Script" ) << false; + QTest::newRow( "Andale Mono" ) << QString( "Andale Mono" ) << true; QTest::newRow( "Courier" ) << QString( "Courier" ) << true; QTest::newRow( "Courier New" ) << QString( "Courier New" ) << true; +#ifndef Q_WS_MAC + QTest::newRow( "Script" ) << QString( "Script" ) << false; QTest::newRow( "Lucida Console" ) << QString( "Lucida Console" ) << true; +#else + QTest::newRow( "Menlo" ) << QString( "Menlo" ) << true; + QTest::newRow( "Monaco" ) << QString( "Monaco" ) << true; +#endif } void tst_QFontDatabase::fixedPitch() @@ -156,6 +167,28 @@ void tst_QFontDatabase::fixedPitch() QCOMPARE(fi.fixedPitch(), fixedPitch); } +#ifdef Q_WS_MAC +void tst_QFontDatabase::trickyFonts_data() +{ + QTest::addColumn("font"); + + QTest::newRow( "Geeza Pro" ) << QString( "Geeza Pro" ); +} + +void tst_QFontDatabase::trickyFonts() +{ + QFETCH(QString, font); + + QFontDatabase fdb; + if (!fdb.families().contains(font)) + QSKIP( "Font not installed", SkipSingle); + + QFont qfont(font); + QFontInfo fi(qfont); + QCOMPARE(fi.family(), font); +} +#endif + void tst_QFontDatabase::widthTwoTimes_data() { QTest::addColumn("font"); diff --git a/tests/auto/qglyphs/tst_qglyphs.cpp b/tests/auto/qglyphs/tst_qglyphs.cpp index b75e801..da91063 100644 --- a/tests/auto/qglyphs/tst_qglyphs.cpp +++ b/tests/auto/qglyphs/tst_qglyphs.cpp @@ -347,10 +347,6 @@ void tst_QGlyphs::drawMultiScriptText1() void tst_QGlyphs::drawMultiScriptText2() { -#if defined(Q_WS_MAC) - QSKIP("Unstable because of QTBUG-11145", SkipAll); -#endif - QString text; text += QChar(0x0621); // Arabic, Hamza text += QChar(0x03D0); // Greek, beta @@ -530,10 +526,6 @@ void tst_QGlyphs::drawUnderlinedText() void tst_QGlyphs::drawRightToLeft() { -#if defined(Q_WS_MAC) - QSKIP("Unstable because of QTBUG-11145", SkipAll); -#endif - QString s; s.append(QChar(1575)); s.append(QChar(1578)); -- cgit v0.12 From d03082dfa2140ebc0d7988e52d29ed06f7698bc2 Mon Sep 17 00:00:00 2001 From: Arvid Ephraim Picciani Date: Fri, 10 Sep 2010 14:45:28 +0200 Subject: test for gdb version before runnning dwarf indexing Reviewed-by: ossi --- mkspecs/features/unix/gdb_dwarf_index.prf | 1 + 1 file changed, 1 insertion(+) diff --git a/mkspecs/features/unix/gdb_dwarf_index.prf b/mkspecs/features/unix/gdb_dwarf_index.prf index 264edc2..e2167a6 100644 --- a/mkspecs/features/unix/gdb_dwarf_index.prf +++ b/mkspecs/features/unix/gdb_dwarf_index.prf @@ -1,6 +1,7 @@ !CONFIG(separate_debug_info):CONFIG(debug, debug|release):!staticlib:!static:!contains(TEMPLATE, subdirs):!isEmpty(QMAKE_OBJCOPY) { QMAKE_GDB_INDEX = { test -z \"$(DESTDIR)\" || cd \"$(DESTDIR)\"; } && \ + test \$\$(gdb --version | sed -e \'s,[^(]*(GDB).\\([0-9]\\)\\.\\([0-9]\\).*,\\1\\2,;q\') -gt 71 && \ gdb --nx --batch --quiet -ex \'set confirm off\' -ex \'save gdb-index .\' -ex quit \'$(TARGET)\' && \ test -f $(TARGET).gdb-index && \ $$QMAKE_OBJCOPY --add-section \'.gdb_index=$(TARGET).gdb-index\' --set-section-flags \'.gdb_index=readonly\' \'$(TARGET)\' \'$(TARGET)\' && \ -- cgit v0.12 From 2fcaa3b6a4f1cb4f41991034317d8c6d50a6b3da Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Mon, 19 Jul 2010 10:52:56 +0200 Subject: Fix typo in declative example. Review-by: trust me --- doc/src/declarative/example-slideswitch.qdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/declarative/example-slideswitch.qdoc b/doc/src/declarative/example-slideswitch.qdoc index 1a40f14..09b6006 100644 --- a/doc/src/declarative/example-slideswitch.qdoc +++ b/doc/src/declarative/example-slideswitch.qdoc @@ -35,7 +35,7 @@ The code for this example can be found in the \c $QTDIR/examples/declarative/ui- \section1 Overview -The elements that composed the switch are: +The elements that compose the switch are: \list \o a \c on property (the interface to interact with the switch), -- cgit v0.12 From 408ff8b676e22a33c962077467d1f48b58286d43 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Fri, 10 Sep 2010 17:14:00 +0200 Subject: Fix typos. list of pointers, not points beter -> better Review-by: trust-me --- doc/src/declarative/advtutorial.qdoc | 2 +- doc/src/declarative/extending.qdoc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/src/declarative/advtutorial.qdoc b/doc/src/declarative/advtutorial.qdoc index 1341bbb..4c97e1a 100644 --- a/doc/src/declarative/advtutorial.qdoc +++ b/doc/src/declarative/advtutorial.qdoc @@ -425,7 +425,7 @@ First we call \c sendHighScore() (explained in the section below) if it is possi Then, we use the \l{Offline Storage API} to maintain a persistant SQL database unique to this application. We create an offline storage database for the high scores using \c openDatabase() and prepare the data and SQL query that we want to use to save it. The offline storage API uses SQL queries for data manipulation and retrival, and in the \c db.transaction() call we use three SQL queries to initialize the database (if necessary), and then add to and retrieve high scores. To use the returned data, we turn it into a string with one line per row returned, and show a dialog containing that string. -This is one way of storing and displaying high scores locally, but certainly not the only way. A more complex alternative would be to create a high score dialog component, and pass it the results for processing and display (instead of reusing the \c Dialog). This would allow a more themeable dialog that could beter present the high scores. If your QML is the UI for a C++ application, you could also have passed the score to a C++ function to store it locally in a variety of ways, including a simple format without SQL or in another SQL database. +This is one way of storing and displaying high scores locally, but certainly not the only way. A more complex alternative would be to create a high score dialog component, and pass it the results for processing and display (instead of reusing the \c Dialog). This would allow a more themeable dialog that could better present the high scores. If your QML is the UI for a C++ application, you could also have passed the score to a C++ function to store it locally in a variety of ways, including a simple format without SQL or in another SQL database. \section3 Storing high scores online diff --git a/doc/src/declarative/extending.qdoc b/doc/src/declarative/extending.qdoc index 5c4d5e7..53db57b 100644 --- a/doc/src/declarative/extending.qdoc +++ b/doc/src/declarative/extending.qdoc @@ -124,7 +124,7 @@ The QML snippet shown above assigns a \c Person object to the \c BirthdayParty's QML can set properties of types that are more complex than basic intrinsics like integers and strings. Properties can also be object pointers, Qt interface -pointers, lists of object points, and lists of Qt interface pointers. As QML +pointers, lists of object pointers, and lists of Qt interface pointers. As QML is typesafe it ensures that only valid types are assigned to these properties, just like it does for primitive types. -- cgit v0.12