diff options
author | Samuel Rødal <samuel.rodal@nokia.com> | 2011-07-07 08:51:52 (GMT) |
---|---|---|
committer | Samuel Rødal <samuel.rodal@nokia.com> | 2011-07-26 08:57:11 (GMT) |
commit | 5842d19cf3dff37a85cbdb31ab5a170ceaf5c7fb (patch) | |
tree | b87af2ff273085a80c491847db05aa50ca979f92 /src/opengl | |
parent | d852e6164a4aaf8e235e97cba8f0e8e9b933987c (diff) | |
download | Qt-5842d19cf3dff37a85cbdb31ab5a170ceaf5c7fb.zip Qt-5842d19cf3dff37a85cbdb31ab5a170ceaf5c7fb.tar.gz Qt-5842d19cf3dff37a85cbdb31ab5a170ceaf5c7fb.tar.bz2 |
Fixed holes in border image drawing by introducing new API.
When rasterizing two adjacent QRectFs it's important that the shared x
or y-edges have the exact same coordinates, or there might be a hole or
an overlapping pixel when they are rasterized. Since the
drawPixmapFragments API was based on a center position and a scale, it
can not be used for this purpose, as the in the situation of two
horizontally adjacent rectangles the right edge of the left-most rect
and the left edge of the right-most edge are computed differently. Thus
rounding errors can cause them to not be equal, especially when there's
also a scaling / translating painter transform involved.
Thus, to not sacrifice performance, we need to introduce a new
drawPixmapFragments API that's simply takes an array of target
rectangles and an array of source rectangles. It should give a slight
performance boost for the border pixmap use case as well, since there
are less floating point multiplications / divisions involved.
Task-number: QTBUG-19079
Reviewed-by: Kim
Diffstat (limited to 'src/opengl')
-rw-r--r-- | src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp | 128 | ||||
-rw-r--r-- | src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h | 10 |
2 files changed, 123 insertions, 15 deletions
diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp index 0d2f2a2..a961366 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp @@ -651,7 +651,7 @@ void QGL2PaintEngineExPrivate::transferMode(EngineMode newMode) if (newMode == mode) return; - if (mode == TextDrawingMode || mode == ImageDrawingMode || mode == ImageArrayDrawingMode) { + if (mode == TextDrawingMode || imageDrawingMode) { lastTextureUsed = GLuint(-1); } @@ -661,14 +661,21 @@ void QGL2PaintEngineExPrivate::transferMode(EngineMode newMode) shaderManager->setHasComplexGeometry(false); } + imageDrawingMode = false; + if (newMode == ImageDrawingMode) { setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, staticVertexCoordinateArray); setVertexAttributePointer(QT_TEXTURE_COORDS_ATTR, staticTextureCoordinateArray); + imageDrawingMode = true; } - if (newMode == ImageArrayDrawingMode) { + if (newMode == ImageArrayDrawingMode || newMode == ImageArrayWithOpacityDrawingMode) { setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, (GLfloat*)vertexCoordinateArray.data()); setVertexAttributePointer(QT_TEXTURE_COORDS_ATTR, (GLfloat*)textureCoordinateArray.data()); + imageDrawingMode = true; + } + + if (newMode == ImageArrayWithOpacityDrawingMode) { setVertexAttributePointer(QT_OPACITY_ATTR, (GLfloat*)opacityArray.data()); } @@ -1096,7 +1103,7 @@ void QGL2PaintEngineExPrivate::resetClipIfNeeded() bool QGL2PaintEngineExPrivate::prepareForDraw(bool srcPixelsAreOpaque) { - if (brushTextureDirty && mode != ImageDrawingMode && mode != ImageArrayDrawingMode) + if (brushTextureDirty && !imageDrawingMode) updateBrushTexture(); if (compositionModeDirty) @@ -1116,12 +1123,12 @@ bool QGL2PaintEngineExPrivate::prepareForDraw(bool srcPixelsAreOpaque) } QGLEngineShaderManager::OpacityMode opacityMode; - if (mode == ImageArrayDrawingMode) { + if (mode == ImageArrayWithOpacityDrawingMode) { opacityMode = QGLEngineShaderManager::AttributeOpacity; } else { opacityMode = stateHasOpacity ? QGLEngineShaderManager::UniformOpacity : QGLEngineShaderManager::NoOpacity; - if (stateHasOpacity && (mode != ImageDrawingMode)) { + if (stateHasOpacity && !imageDrawingMode) { // Using a brush bool brushIsPattern = (currentBrush.style() >= Qt::Dense1Pattern) && (currentBrush.style() <= Qt::DiagCrossPattern); @@ -1141,7 +1148,7 @@ bool QGL2PaintEngineExPrivate::prepareForDraw(bool srcPixelsAreOpaque) matrixUniformDirty = true; } - if (brushUniformsDirty && mode != ImageDrawingMode && mode != ImageArrayDrawingMode) + if (brushUniformsDirty && !imageDrawingMode) updateBrushUniforms(); if (opacityMode == QGLEngineShaderManager::UniformOpacity && opacityUniformDirty) { @@ -1881,23 +1888,46 @@ void QGL2PaintEngineEx::drawPixmapFragments(const QPainter::PixmapFragment *frag return; } + QSize size = pixmap.size(); + ensureActive(); int max_texture_size = d->ctx->d_func()->maxTextureSize(); - if (pixmap.width() > max_texture_size || pixmap.height() > max_texture_size) { + if (size.width() > max_texture_size || size.height() > max_texture_size) { QPixmap scaled = pixmap.scaled(max_texture_size, max_texture_size, Qt::KeepAspectRatio); - d->drawPixmapFragments(fragments, fragmentCount, scaled, hints); + d->drawPixmapFragments(fragments, fragmentCount, scaled, size, hints); } else { - d->drawPixmapFragments(fragments, fragmentCount, pixmap, hints); + d->drawPixmapFragments(fragments, fragmentCount, pixmap, size, hints); } } +void QGL2PaintEngineEx::drawPixmapFragments(const QRectF *targetRects, const QRectF *sourceRects, int fragmentCount, const QPixmap &pixmap, + QPainter::PixmapFragmentHints hints) +{ + Q_D(QGL2PaintEngineEx); + // Use fallback for extended composition modes. + if (state()->composition_mode > QPainter::CompositionMode_Plus) { + QPaintEngineEx::drawPixmapFragments(targetRects, sourceRects, fragmentCount, pixmap, hints); + return; + } + + QSize size = pixmap.size(); + + ensureActive(); + int max_texture_size = d->ctx->d_func()->maxTextureSize(); + if (size.width() > max_texture_size || size.height() > max_texture_size) { + QPixmap scaled = pixmap.scaled(max_texture_size, max_texture_size, Qt::KeepAspectRatio); + d->drawPixmapFragments(targetRects, sourceRects, fragmentCount, scaled, size, hints); + } else { + d->drawPixmapFragments(targetRects, sourceRects, fragmentCount, pixmap, size, hints); + } +} void QGL2PaintEngineExPrivate::drawPixmapFragments(const QPainter::PixmapFragment *fragments, int fragmentCount, const QPixmap &pixmap, - QPainter::PixmapFragmentHints hints) + const QSize &size, QPainter::PixmapFragmentHints hints) { - GLfloat dx = 1.0f / pixmap.size().width(); - GLfloat dy = 1.0f / pixmap.size().height(); + GLfloat dx = 1.0f / size.width(); + GLfloat dy = 1.0f / size.height(); vertexCoordinateArray.clear(); textureCoordinateArray.clear(); @@ -1958,7 +1988,7 @@ void QGL2PaintEngineExPrivate::drawPixmapFragments(const QPainter::PixmapFragmen data[i].y = 1 - data[i].y; } - transferMode(ImageArrayDrawingMode); + transferMode(allOpaque ? ImageArrayDrawingMode : ImageArrayWithOpacityDrawingMode); bool isBitmap = pixmap.isQBitmap(); bool isOpaque = !isBitmap && (!pixmap.hasAlpha() || (hints & QPainter::OpaqueHint)) && allOpaque; @@ -1981,6 +2011,77 @@ void QGL2PaintEngineExPrivate::drawPixmapFragments(const QPainter::PixmapFragmen glDrawArrays(GL_TRIANGLES, 0, 6 * fragmentCount); } +void QGL2PaintEngineExPrivate::drawPixmapFragments(const QRectF *targetRects, const QRectF *sourceRects, int fragmentCount, + const QPixmap &pixmap, const QSize &size, + QPainter::PixmapFragmentHints hints) +{ + GLfloat dx = 1.0f / size.width(); + GLfloat dy = 1.0f / size.height(); + + vertexCoordinateArray.clear(); + textureCoordinateArray.clear(); + + if (snapToPixelGrid) { + snapToPixelGrid = false; + matrixDirty = true; + } + + for (int i = 0; i < fragmentCount; ++i) { + vertexCoordinateArray.addVertex(targetRects[i].right(), targetRects[i].bottom()); + vertexCoordinateArray.addVertex(targetRects[i].right(), targetRects[i].top()); + vertexCoordinateArray.addVertex(targetRects[i].left(), targetRects[i].top()); + vertexCoordinateArray.addVertex(targetRects[i].left(), targetRects[i].top()); + vertexCoordinateArray.addVertex(targetRects[i].left(), targetRects[i].bottom()); + vertexCoordinateArray.addVertex(targetRects[i].right(), targetRects[i].bottom()); + + QRectF sourceRect = sourceRects ? sourceRects[i] : QRectF(0, 0, size.width(), size.height()); + + QGLRect src(sourceRect.left() * dx, sourceRect.top() * dy, + sourceRect.right() * dx, sourceRect.bottom() * dy); + + textureCoordinateArray.addVertex(src.right, src.bottom); + textureCoordinateArray.addVertex(src.right, src.top); + textureCoordinateArray.addVertex(src.left, src.top); + textureCoordinateArray.addVertex(src.left, src.top); + textureCoordinateArray.addVertex(src.left, src.bottom); + textureCoordinateArray.addVertex(src.right, src.bottom); + } + + glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT); + QGLTexture *texture = ctx->d_func()->bindTexture(pixmap, GL_TEXTURE_2D, GL_RGBA, + QGLContext::InternalBindOption + | QGLContext::CanFlipNativePixmapBindOption); + + if (texture->options & QGLContext::InvertedYBindOption) { + // Flip texture y-coordinate. + QGLPoint *data = textureCoordinateArray.data(); + for (int i = 0; i < 6 * fragmentCount; ++i) + data[i].y = 1 - data[i].y; + } + + transferMode(ImageArrayDrawingMode); + + bool isBitmap = pixmap.isQBitmap(); + bool isOpaque = !isBitmap && (!pixmap.hasAlpha() || (hints & QPainter::OpaqueHint)); + + updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE, + q->state()->renderHints & QPainter::SmoothPixmapTransform, texture->id); + + // Setup for texture drawing + currentBrush = noBrush; + shaderManager->setSrcPixelType(isBitmap ? QGLEngineShaderManager::PatternSrc + : QGLEngineShaderManager::ImageSrc); + if (prepareForDraw(isOpaque)) + shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::ImageTexture), QT_IMAGE_TEXTURE_UNIT); + + if (isBitmap) { + QColor col = qt_premultiplyColor(q->state()->pen.color(), (GLfloat)q->state()->opacity); + shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::PatternColor), col); + } + + glDrawArrays(GL_TRIANGLES, 0, 6 * fragmentCount); +} + bool QGL2PaintEngineEx::begin(QPaintDevice *pdev) { Q_D(QGL2PaintEngineEx); @@ -2001,6 +2102,7 @@ bool QGL2PaintEngineEx::begin(QPaintDevice *pdev) d->width = sz.width(); d->height = sz.height(); d->mode = BrushDrawingMode; + d->imageDrawingMode = false; d->brushTextureDirty = true; d->brushUniformsDirty = true; d->matrixUniformDirty = true; diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h index 2895d5a..80daf63 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h @@ -68,7 +68,8 @@ enum EngineMode { ImageDrawingMode, TextDrawingMode, BrushDrawingMode, - ImageArrayDrawingMode + ImageArrayDrawingMode, + ImageArrayWithOpacityDrawingMode }; QT_BEGIN_NAMESPACE @@ -126,6 +127,8 @@ public: virtual void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr); virtual void drawPixmapFragments(const QPainter::PixmapFragment *fragments, int fragmentCount, const QPixmap &pixmap, QPainter::PixmapFragmentHints hints); + virtual void drawPixmapFragments(const QRectF *targetRects, const QRectF *sourceRects, int fragmentCount, const QPixmap &pixmap, + QPainter::PixmapFragmentHints hints); virtual void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, Qt::ImageConversionFlags flags = Qt::AutoColor); virtual void drawTextItem(const QPointF &p, const QTextItem &textItem); @@ -202,7 +205,9 @@ public: void fill(const QVectorPath &path); void stroke(const QVectorPath &path, const QPen &pen); void drawTexture(const QGLRect& dest, const QGLRect& src, const QSize &textureSize, bool opaque, bool pattern = false); - void drawPixmapFragments(const QPainter::PixmapFragment *fragments, int fragmentCount, const QPixmap &pixmap, + void drawPixmapFragments(const QPainter::PixmapFragment *fragments, int fragmentCount, const QPixmap &pixmap, const QSize &size, + QPainter::PixmapFragmentHints hints); + void drawPixmapFragments(const QRectF *targetRects, const QRectF *sourceRects, int fragmentCount, const QPixmap &pixmap, const QSize &size, QPainter::PixmapFragmentHints hints); void drawCachedGlyphs(QFontEngineGlyphCache::Type glyphType, QStaticTextItem *staticTextItem); @@ -255,6 +260,7 @@ public: int width, height; QGLContext *ctx; EngineMode mode; + bool imageDrawingMode; QFontEngineGlyphCache::Type glyphCacheType; // Dirty flags |