Improvements to Image element
authorYann Bodson <yann.bodson@nokia.com>
Tue, 5 Jul 2011 03:06:54 +0000 (13:06 +1000)
committerQt by Nokia <qt-info@nokia.com>
Wed, 20 Jul 2011 01:07:06 +0000 (03:07 +0200)
- add Image.Pad as a fillMode
- add horizontal and vertical alignment properties

Task-number: QTBUG-18291
Change-Id: Iaaf3b2d02c47ad01d2c8b49d146f1a9401b2558d
Reviewed-on: http://codereview.qt.nokia.com/1468
Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com>
Reviewed-by: Martin Jones <martin.jones@nokia.com>
Reviewed-by: Yann Bodson <yann.bodson@nokia.com>
src/declarative/items/qsgimage.cpp
src/declarative/items/qsgimage_p.h
src/declarative/items/qsgimage_p_p.h
tests/systemtests/declarative/qsgimage/ImageNG.qml [new file with mode: 0644]
tests/systemtests/declarative/qsgimage/img-align.qml [new file with mode: 0644]
tests/systemtests/declarative/qsgimage/qt-logo.png [new file with mode: 0644]

index ee4ea2b..b77a654 100644 (file)
@@ -46,6 +46,7 @@
 #include <private/qsgadaptationlayer_p.h>
 
 #include <QtGui/qpainter.h>
+#include <qmath.h>
 
 QT_BEGIN_NAMESPACE
 
@@ -54,6 +55,8 @@ QSGImagePrivate::QSGImagePrivate()
     , paintedWidth(0)
     , paintedHeight(0)
     , pixmapChanged(false)
+    , hAlign(QSGImage::AlignHCenter)
+    , vAlign(QSGImage::AlignVCenter)
 {
 }
 
@@ -155,6 +158,9 @@ void QSGImage::updatePaintedGeometry()
 
         d->paintedHeight = heightScale * qreal(d->pix.height());
         d->paintedWidth = widthScale * qreal(d->pix.width());
+    } else if (d->fillMode == Pad) {
+        d->paintedWidth = d->pix.width();
+        d->paintedHeight = d->pix.height();
     } else {
         d->paintedWidth = width();
         d->paintedHeight = height();
@@ -218,6 +224,21 @@ QSGNode *QSGImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
     QSGTexture::WrapMode hWrap = QSGTexture::ClampToEdge;
     QSGTexture::WrapMode vWrap = QSGTexture::ClampToEdge;
 
+    qreal pixWidth = (d->fillMode == PreserveAspectFit) ? d->paintedWidth : d->pix.width();
+    qreal pixHeight = (d->fillMode == PreserveAspectFit) ? d->paintedHeight : d->pix.height();
+
+    int xOffset = 0;
+    if (d->hAlign == QSGImage::AlignHCenter)
+        xOffset = qCeil((width() - pixWidth) / 2.);
+    else if (d->hAlign == QSGImage::AlignRight)
+        xOffset = qCeil(width() - pixWidth);
+
+    int yOffset = 0;
+    if (d->vAlign == QSGImage::AlignVCenter)
+        yOffset = qCeil((height() - pixHeight) / 2.);
+    else if (d->vAlign == QSGImage::AlignBottom)
+        yOffset = qCeil(height() - pixHeight);
+
     switch (d->fillMode) {
     default:
     case Stretch:
@@ -226,8 +247,7 @@ QSGNode *QSGImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
         break;
 
     case PreserveAspectFit:
-        targetRect = QRectF((width() - d->paintedWidth) / 2., (height() - d->paintedHeight) / 2.,
-                            d->paintedWidth, d->paintedHeight);
+        targetRect = QRectF(xOffset, yOffset, d->paintedWidth, d->paintedHeight);
         sourceRect = d->pix.rect();
         break;
 
@@ -238,33 +258,52 @@ QSGNode *QSGImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
 
         if (wscale > hscale) {
             int src = (hscale / wscale) * qreal(d->pix.height());
-            sourceRect = QRectF(0, (d->pix.height() - src) / 2, d->pix.width(), src);
+            int y = 0;
+            if (d->vAlign == QSGImage::AlignVCenter)
+                y = qCeil((d->pix.height() - src) / 2.);
+            else if (d->vAlign == QSGImage::AlignBottom)
+                y = qCeil(d->pix.height() - src);
+            sourceRect = QRectF(0, y, d->pix.width(), src);
+
         } else {
             int src = (wscale / hscale) * qreal(d->pix.width());
-            sourceRect = QRectF((d->pix.width() - src) / 2, 0, src, d->pix.height());
+            int x = 0;
+            if (d->hAlign == QSGImage::AlignHCenter)
+                x = qCeil((d->pix.width() - src) / 2.);
+            else if (d->hAlign == QSGImage::AlignRight)
+                x = qCeil(d->pix.width() - src);
+            sourceRect = QRectF(x, 0, src, d->pix.height());
+        }
         }
-    }
         break;
 
     case Tile:
         targetRect = QRectF(0, 0, width(), height());
-        sourceRect = QRectF(0, 0, width(), height());
+        sourceRect = QRectF(-xOffset, -yOffset, width(), height());
         hWrap = QSGTexture::Repeat;
         vWrap = QSGTexture::Repeat;
         break;
 
     case TileHorizontally:
         targetRect = QRectF(0, 0, width(), height());
-        sourceRect = QRectF(0, 0, width(), d->pix.height());
+        sourceRect = QRectF(-xOffset, 0, width(), d->pix.height());
         hWrap = QSGTexture::Repeat;
         break;
 
     case TileVertically:
         targetRect = QRectF(0, 0, width(), height());
-        sourceRect = QRectF(0, 0, d->pix.width(), height());
+        sourceRect = QRectF(0, -yOffset, d->pix.width(), height());
         vWrap = QSGTexture::Repeat;
         break;
 
+    case Pad:
+        qreal w = qMin(qreal(d->pix.width()), width());
+        qreal h = qMin(qreal(d->pix.height()), height());
+        qreal x = (d->pix.width() > width()) ? -xOffset : 0;
+        qreal y = (d->pix.height() > height()) ? -yOffset : 0;
+        targetRect = QRectF(x + xOffset, y + yOffset, w, h);
+        sourceRect = QRectF(x, y, w, h);
+        break;
     };
 
     QRectF nsrect(sourceRect.x() / d->pix.width(),
@@ -302,4 +341,40 @@ void QSGImage::pixmapChange()
     d->pixmapChanged = true;
 }
 
+QSGImage::VAlignment QSGImage::verticalAlignment() const
+{
+    Q_D(const QSGImage);
+    return d->vAlign;
+}
+
+void QSGImage::setVerticalAlignment(VAlignment align)
+{
+    Q_D(QSGImage);
+    if (d->vAlign == align)
+        return;
+
+    d->vAlign = align;
+    update();
+    updatePaintedGeometry();
+    emit verticalAlignmentChanged(align);
+}
+
+QSGImage::HAlignment QSGImage::horizontalAlignment() const
+{
+    Q_D(const QSGImage);
+    return d->hAlign;
+}
+
+void QSGImage::setHorizontalAlignment(HAlignment align)
+{
+    Q_D(QSGImage);
+    if (d->hAlign == align)
+        return;
+
+    d->hAlign = align;
+    update();
+    updatePaintedGeometry();
+    emit horizontalAlignmentChanged(align);
+}
+
 QT_END_NAMESPACE
index 7fc2940..4faf96d 100644 (file)
@@ -57,11 +57,15 @@ class Q_AUTOTEST_EXPORT QSGImage : public QSGImageBase, public QSGTextureProvide
 {
     Q_OBJECT
     Q_ENUMS(FillMode)
+    Q_ENUMS(HAlignment)
+    Q_ENUMS(VAlignment)
 
     Q_PROPERTY(FillMode fillMode READ fillMode WRITE setFillMode NOTIFY fillModeChanged)
     Q_PROPERTY(qreal paintedWidth READ paintedWidth NOTIFY paintedGeometryChanged)
     Q_PROPERTY(qreal paintedHeight READ paintedHeight NOTIFY paintedGeometryChanged)
     Q_PROPERTY(QSGTexture *texture READ texture)
+    Q_PROPERTY(HAlignment horizontalAlignment READ horizontalAlignment WRITE setHorizontalAlignment NOTIFY horizontalAlignmentChanged)
+    Q_PROPERTY(VAlignment verticalAlignment READ verticalAlignment WRITE setVerticalAlignment NOTIFY verticalAlignmentChanged)
 
     Q_INTERFACES(QSGTextureProvider)
 
@@ -69,7 +73,15 @@ public:
     QSGImage(QSGItem *parent=0);
     ~QSGImage();
 
-    enum FillMode { Stretch, PreserveAspectFit, PreserveAspectCrop, Tile, TileVertically, TileHorizontally };
+    enum HAlignment { AlignLeft = Qt::AlignLeft,
+                       AlignRight = Qt::AlignRight,
+                       AlignHCenter = Qt::AlignHCenter };
+    enum VAlignment { AlignTop = Qt::AlignTop,
+                       AlignBottom = Qt::AlignBottom,
+                       AlignVCenter = Qt::AlignVCenter };
+
+    enum FillMode { Stretch, PreserveAspectFit, PreserveAspectCrop, Tile, TileVertically, TileHorizontally, Pad };
+
     FillMode fillMode() const;
     void setFillMode(FillMode);
 
@@ -80,9 +92,17 @@ public:
 
     virtual QSGTexture *texture() const;
 
+    HAlignment horizontalAlignment() const;
+    void setHorizontalAlignment(HAlignment align);
+
+    VAlignment verticalAlignment() const;
+    void setVerticalAlignment(VAlignment align);
+
 Q_SIGNALS:
     void fillModeChanged();
     void paintedGeometryChanged();
+    void horizontalAlignmentChanged(HAlignment alignment);
+    void verticalAlignmentChanged(VAlignment alignment);
 
 protected:
     QSGImage(QSGImagePrivate &dd, QSGItem *parent);
index 0d0f2ee..d0b1099 100644 (file)
@@ -74,6 +74,8 @@ public:
     void setPixmap(const QPixmap &pix);
 
     bool pixmapChanged : 1;
+    QSGImage::HAlignment hAlign;
+    QSGImage::VAlignment vAlign;
 };
 
 QT_END_NAMESPACE
diff --git a/tests/systemtests/declarative/qsgimage/ImageNG.qml b/tests/systemtests/declarative/qsgimage/ImageNG.qml
new file mode 100644 (file)
index 0000000..4a10910
--- /dev/null
@@ -0,0 +1,19 @@
+import QtQuick 2.0
+
+Image {
+    property bool explicitSize: true
+    property alias label: lb.text
+
+    width: explicitSize ? 200 : undefined; height: explicitSize ? 150 : undefined
+    smooth: true
+
+    Rectangle {
+        border.color: "red"; color: "transparent"
+        anchors.fill: parent
+    }
+
+    Text {
+        id: lb
+        anchors.top: parent.bottom; anchors.horizontalCenter: parent.horizontalCenter; anchors.topMargin: 4
+    }
+}
diff --git a/tests/systemtests/declarative/qsgimage/img-align.qml b/tests/systemtests/declarative/qsgimage/img-align.qml
new file mode 100644 (file)
index 0000000..55ee112
--- /dev/null
@@ -0,0 +1,92 @@
+import QtQuick 2.0
+
+Rectangle {
+    id: main
+    width: 800; height: 600
+    focus: true
+    color: "#eeeeee"
+
+    property variant hAlign: Image.AlignHCenter
+    property variant vAlign: Image.AlignVCenter
+    property bool mirror: false
+    property string source: "qt-logo.png"
+
+    Flow {
+        anchors.fill: parent
+        anchors { topMargin: 20; leftMargin: 20; rightMargin: 20 }
+        spacing: 30
+
+        ImageNG {
+            horizontalAlignment: main.hAlign; verticalAlignment: main.vAlign
+            mirror: main.mirror; source: main.source
+            explicitSize: false
+            label: "implicit size"
+        }
+        ImageNG {
+            horizontalAlignment: main.hAlign; verticalAlignment: main.vAlign
+            mirror: main.mirror; source: main.source
+            label: "explicit size"
+        }
+        ImageNG {
+            horizontalAlignment: main.hAlign; verticalAlignment: main.vAlign
+            fillMode: Image.Pad
+            mirror: main.mirror; source: main.source
+            label: "padding"
+        }
+        ImageNG {
+            horizontalAlignment: main.hAlign; verticalAlignment: main.vAlign
+            fillMode: Image.Tile
+            mirror: main.mirror; source: main.source
+            label: "tile"
+        }
+        ImageNG {
+            horizontalAlignment: main.hAlign; verticalAlignment: main.vAlign
+            fillMode: Image.TileHorizontally
+            mirror: main.mirror; source: main.source
+            label: "tile horizontally"
+        }
+        ImageNG {
+            horizontalAlignment: main.hAlign; verticalAlignment: main.vAlign
+            fillMode: Image.TileVertically
+            mirror: main.mirror; source: main.source
+            label: "tile vertically"
+        }
+        ImageNG {
+            horizontalAlignment: main.hAlign; verticalAlignment: main.vAlign
+            fillMode: Image.PreserveAspectFit
+            mirror: main.mirror; source: main.source
+            label: "preserve aspect fit"
+        }
+        ImageNG {
+            horizontalAlignment: main.hAlign; verticalAlignment: main.vAlign
+            fillMode: Image.PreserveAspectFit
+            width: 150; height: 200
+            mirror: main.mirror; source: main.source
+            label: "preserve aspect fit"
+        }
+        ImageNG {
+            horizontalAlignment: main.hAlign; verticalAlignment: main.vAlign
+            fillMode: Image.PreserveAspectCrop
+            mirror: main.mirror; source: main.source
+            label: "preserve aspect crop"
+        }
+        ImageNG {
+            horizontalAlignment: main.hAlign; verticalAlignment: main.vAlign
+            fillMode: Image.PreserveAspectCrop
+            width: 150; height: 200
+            mirror: main.mirror; source: main.source
+            label: "preserve aspect crop"
+        }
+    }
+
+    Keys.onUpPressed: vAlign = Image.AlignTop
+    Keys.onDownPressed: vAlign = Image.AlignBottom
+    Keys.onLeftPressed: hAlign = Image.AlignLeft
+    Keys.onRightPressed: hAlign = Image.AlignRight
+    Keys.onPressed: {
+        if (event.key == Qt.Key_H)
+            hAlign = Image.AlignHCenter
+        else if (event.key == Qt.Key_V)
+            vAlign = Image.AlignVCenter
+    }
+}
diff --git a/tests/systemtests/declarative/qsgimage/qt-logo.png b/tests/systemtests/declarative/qsgimage/qt-logo.png
new file mode 100644 (file)
index 0000000..14ddf2a
Binary files /dev/null and b/tests/systemtests/declarative/qsgimage/qt-logo.png differ