X and y offset on tiled images



  • I run into a problem when making a game, I want to do a parallax background but when I set an image with the Tile property I couldn't move the image while maintaining the Image object on the same position.

    So I hacked the source code of the qml image object to have two new properties: xTileOffset and yTileOffset.

    512kb video showing red balls being animated moving to the side:
    http://real.purpleorangegames.com/wp-content/uploads/2014/05/TestingTileOffset2.mp4
    Vimeo version of the video:
    https://vimeo.com/96127913
    (It is still being processed at the time I published this post)

    And the code is quite simple:
    @ Image
    {
    anchors.fill: parent
    source: "Images/Background/AnimCircle/10.png"
    fillMode: Image.Tile
    SequentialAnimation on xTileOffset
    {
    running: true
    loops: Animation.Infinite
    NumberAnimation { to: 101; duration: 500 }
    }
    }@

    The image covers the parent, the entire window.
    It has 101x101 pixels, it's a red ball on a transparent background.
    So when I animate the xTileOffset it looks like I'm moving the Image object to the side but the Image actually remains at the same place.

    I modified the following files in QtDeclarative/src/quick/items/ :
    qquickimage_p_p.h
    qquickimage_p.h
    qquickimage.cpp

    Adding to qquickimage_p_p.h the variables on the class QQuickImagePrivate:
    @qreal xTileOffset;
    qreal yTileOffset;@

    Then I added to qquickimage_p.h the following to the class QQuickImage:
    @ Q_PROPERTY(qreal xTileOffset READ xTileOffset WRITE setXTileOffset NOTIFY xTileOffsetChanged)
    Q_PROPERTY(qreal yTileOffset READ yTileOffset WRITE setYTileOffset NOTIFY yTileOffsetChanged)

    public:
    qreal xTileOffset() const;
    qreal yTileOffset() const;
    void setXTileOffset(qreal value);
    void setYTileOffset(qreal value);

    Q_SIGNALS:
    void xTileOffsetChanged();
    void yTileOffsetChanged();
    @
    And finally I added to qquickimage.cpp:
    @qreal QQuickImage::xTileOffset() const
    {
    Q_D(const QQuickImage);
    return d->xTileOffset;
    }

    qreal QQuickImage::yTileOffset() const
    {
    Q_D(const QQuickImage);
    return d->yTileOffset;
    }

    void QQuickImage::setXTileOffset(qreal value)
    {
    Q_D(QQuickImage);
    if (d->xTileOffset == value)
    return;

    d->xTileOffset = value;
    update();
    updatePaintedGeometry();
    emit xTileOffsetChanged();
    

    }

    void QQuickImage::setYTileOffset(qreal value)
    {
    Q_D(QQuickImage);
    if (d->yTileOffset == value)
    return;

    d->yTileOffset = value;
    update();
    updatePaintedGeometry();
    emit yTileOffsetChanged();
    

    }@

    And the most important part, inside the function
    @QSGNode *QQuickImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)@
    you can see the variables that I created (xTileOffset and yTileOffset):

    @ case Tile:
    targetRect = QRectF(0, 0, width(), height());
    sourceRect = QRectF(-xOffset+xTileOffset(), -yOffset+yTileOffset(), width(), height());
    hWrap = QSGTexture::Repeat;
    vWrap = QSGTexture::Repeat;
    break;

    case TileHorizontally:
        targetRect = QRectF(0, 0, width(), height());
        sourceRect = QRectF(-xOffset+xTileOffset(), 0, width(), d->pix.height());
        hWrap = QSGTexture::Repeat;
        break;
    
    case TileVertically:
        targetRect = QRectF(0, 0, width(), height());
        sourceRect = QRectF(0, -yOffset+yTileOffset(), d->pix.width(), height());
        vWrap = QSGTexture::Repeat;
        break;@
    

    And that's it.

    Should I try to submit it back to Qt?



  • You could have the same effect by centering the image to the parent item and then modifying the horizontalCenterOffset and verticalCenterOffset properties of the anchors.



  • Sorry, I did a few tests and could not achieve the same result with the option you mentioned, can you provide a minimal example of it?

    The requirements are that a image must remain the same size, at the same place and be Tiled. The real image is just one red ball.

    EDIT:
    I tested some more and came up with this:
    http://purpleorangegames.com/wp-content/uploads/2014/05/TestingTileOffset3.mp4
    @ Image
    {
    width: parent.width
    height: parent.height
    anchors.centerIn: parent
    source: "Images/Background/AnimCircle/10.png"
    fillMode: Image.Tile
    SequentialAnimation on anchors.horizontalCenterOffset
    {
    running: true
    loops: Animation.Infinite
    NumberAnimation { to: -101; duration: 500 }
    }
    }@
    I can't set anchors.fill, instead I need to manually set width and height, the only thing that didn't come out correctly is that at the left side you can see the ball popping up from nowhere instead of coming from the side.

    So I increased the size of the Image to two times the original image (101 pixels):
    http://purpleorangegames.com/wp-content/uploads/2014/05/TestingTileOffset3.mp4
    @ Image
    {
    width: parent.width+202
    height: parent.height
    anchors.centerIn: parent
    source: "Images/Background/AnimCircle/10.png"
    fillMode: Image.Tile
    SequentialAnimation on anchors.horizontalCenterOffset
    {
    running: true
    loops: Animation.Infinite
    NumberAnimation { to: -101; duration: 500 }
    }
    }@

    My goal is to use small images (little stars) and one huge image (a galaxy probably 4000x4000 pixels) and make a parallax background of it.
    I will test this solution with a huge image to see it works as expected.
    Thanks for the tip!



  • I'm sorry, the purpose of Image is display an static image. However it has some properties to fill a large area like tiled.

    I've no experience with that, but if you need to scroll elements on the screen, perhaps a Canvas could help to you.

    http://qt-project.org/doc/qt-5/qml-qtquick-canvas.html
    .



  • Thanks, I will check it out.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.