summaryrefslogtreecommitdiffstats
path: root/src/corelib/doc/snippets/hellotrmain.cpp
blob: 7e09c58f4f15bd692b5a0d78ab89f3b548b0631a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
**   * Redistributions of source code must retain the above copyright
**     notice, this list of conditions and the following disclaimer.
**   * Redistributions in binary form must reproduce the above copyright
**     notice, this list of conditions and the following disclaimer in
**     the documentation and/or other materials provided with the
**     distribution.
**   * Neither the name of The Qt Company Ltd nor the names of its
**     contributors may be used to endorse or promote products derived
**     from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/

//! [0]
int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QTranslator translator;
    // look up e.g. :/translations/myapp_de.qm
    if (translator.load(QLocale(), QLatin1String("myapp"), QLatin1String("_"), QLatin1String(":/translations")))
        app.installTranslator(&translator);

    QPushButton hello(QCoreApplication::translate("main", "Hello world!"));
    hello.resize(100, 30);

    hello.show();
    return app.exec();
}
//! [0]

284' href='#n284'>284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qquickspriteengine_p.h"
#include "qquicksprite_p.h"
#include <qqmlinfo.h>
#include <qqml.h>
#include <QDebug>
#include <QPainter>
#include <QSet>
#include <QtGui/qopengl.h>
#include <QOpenGLFunctions>

QT_BEGIN_NAMESPACE

/*
    \internal Stochastic/Sprite engine implementation docs

    Nomenclature: 'thing' refers to an instance of a running sprite or state. It could be renamed.
    States and Transitions are referred to in the state machine sense here, NOT in the QML sense.

    The Stochastic State engine takes states with stochastic state transitions defined and transitions them.
    When a state is started, it's added to a list of pending updates sorted by their time they want to update.
    An external driver calls the update function with an elapsed time, which becomes the new time offset.
    The pending update stack is popped until all entries are past the current time, which simulates all intervening time.

    The Sprite Engine subclass has two major differences. Firstly all states are sprites (and there's a new vector with them
    cast to sprite). Secondly, it chops up images and states to fit a texture friendly format.
    Before the Sprite Engine starts running, its user requests a texture assembled from all the sprite images. This
    texture is made by pasting the sprites into one image, with one sprite animation per row (in the future it is planned to have
    arbitrary X/Y start ends, but they will still be assembled and recorded here and still have to be contiguous lines).
    This cut-up allows the users to calcuate frame positions with a texture percentage width and elapsed time.
    It also means that large sprites cover multiple lines to fit inside the texture memory limit (which is a square).

    Large sprites covering multiple lines breaks this simple interface for the users, so each line is treated as a pseudostate
    and it's mostly hidden from the spriteengine users (except that they'll get advanced signals where the state is the same
    but the visual parameters changed). These are not real states because that would get very complex with bindings. Instead,
    when sprite attributes are requested from a sprite that has multiple pseudostates, it returns the values for the psuedostate
    it is in. State advancement is intercepted and hollow for pseudostates, except the last one. The last one transitions as the
    state normally does.
*/

static const int NINF = -1000000;//magic number for random start time - should be more negative than a single realistic animation duration
//#define SPRITE_IMAGE_DEBUG
#ifdef SPRITE_IMAGE_DEBUG
#include <QFile>
#include <QDir>
#endif
/* TODO:
   make sharable?
   solve the state data initialization/transfer issue so as to not need to make friends
*/

QQuickStochasticEngine::QQuickStochasticEngine(QObject *parent) :
    QObject(parent), m_timeOffset(0), m_addAdvance(false)
{
    //Default size 1
    setCount(1);
}

QQuickStochasticEngine::QQuickStochasticEngine(const QList<QQuickStochasticState *> &states, QObject *parent) :
    QObject(parent), m_states(states), m_timeOffset(0), m_addAdvance(false)
{
    //Default size 1
    setCount(1);
}

QQuickStochasticEngine::~QQuickStochasticEngine()
{
}

QQuickSpriteEngine::QQuickSpriteEngine(QObject *parent)
    : QQuickStochasticEngine(parent), m_startedImageAssembly(false), m_loaded(false)
{
}

QQuickSpriteEngine::QQuickSpriteEngine(const QList<QQuickSprite *> &sprites, QObject *parent)
    : QQuickSpriteEngine(parent)
{
    for (QQuickSprite* sprite : sprites)
        m_states << (QQuickStochasticState*)sprite;
}

QQuickSpriteEngine::~QQuickSpriteEngine()
{
}


int QQuickSpriteEngine::maxFrames() const
{
    return m_maxFrames;
}

/* States too large to fit in one row are split into multiple rows
   This is more efficient for the implementation, but should remain an implementation detail (invisible from QML)
   Therefore the below functions abstract sprite from the viewpoint of classes that pass the details onto shaders
   But States maintain their listed index for internal structures
TODO: All these calculations should be pre-calculated and cached during initialization for a significant performance boost
TODO: Above idea needs to have the varying duration offset added to it
*/
//TODO: Should these be adding advanceTime as well? But only if advanceTime was added to your startTime...
/*
    To get these working with duration=-1, m_startTimes will be messed with should duration=-1
    m_startTimes will be set in advance/restart to 0->(m_framesPerRow-1) and can be used directly as extra.
    This makes it 'frame' instead, but is more memory efficient than two arrays and less hideous than a vector of unions.
*/
int QQuickSpriteEngine::pseudospriteProgress(int sprite, int state, int* rowDuration) const
{
    int myRowDuration = m_duration[sprite] * m_sprites[state]->m_framesPerRow / m_sprites[state]->m_frames;
    if (rowDuration)
        *rowDuration = myRowDuration;

    if (m_sprites[state]->reverse()) //shift start-time back by the amount of time the first frame is smaller than rowDuration
        return (m_timeOffset - (m_startTimes[sprite] - (myRowDuration - (m_duration[sprite] % myRowDuration))) )
                    / myRowDuration;
    else
        return (m_timeOffset - m_startTimes[sprite]) / myRowDuration;
}

int QQuickSpriteEngine::spriteState(int sprite) const
{
    if (!m_loaded)
        return 0;
    int state = m_things[sprite];
    if (!m_sprites[state]->m_generatedCount)
        return state;

    int extra;
    if (m_sprites[state]->frameSync())
        extra = m_startTimes[sprite];
    else if (!m_duration[sprite])
        return state;
    else
        extra = pseudospriteProgress(sprite, state);
    if (m_sprites[state]->reverse())
        extra = (m_sprites[state]->m_generatedCount - 1) - extra;

    return state + extra;
}

int QQuickSpriteEngine::spriteStart(int sprite) const
{
    if (!m_duration[sprite] || !m_loaded)
        return m_timeOffset;
    int state = m_things[sprite];
    if (!m_sprites[state]->m_generatedCount)
        return m_startTimes[sprite];
    int rowDuration;
    int extra = pseudospriteProgress(sprite, state, &rowDuration);
    if (m_sprites[state]->reverse())
        return m_startTimes[sprite] + (extra ? (extra - 1)*rowDuration + (m_duration[sprite] % rowDuration) : 0);
    return m_startTimes[sprite] + extra*rowDuration;
}

int QQuickSpriteEngine::spriteFrames(int sprite) const
{
    if (!m_loaded)
        return 1;
    int state = m_things[sprite];
    if (!m_sprites[state]->m_generatedCount)
        return m_sprites[state]->frames();

    int extra;
    if (m_sprites[state]->frameSync())
        extra = m_startTimes[sprite];
    else if (!m_duration[sprite])
        return m_sprites[state]->frames();
    else
        extra = pseudospriteProgress(sprite, state);
    if (m_sprites[state]->reverse())
        extra = (m_sprites[state]->m_generatedCount - 1) - extra;


    if (extra == m_sprites[state]->m_generatedCount - 1) {//last state
        const int framesRemaining = m_sprites[state]->frames() % m_sprites[state]->m_framesPerRow;
        if (framesRemaining > 0)
            return framesRemaining;
    }
    return m_sprites[state]->m_framesPerRow;
}

int QQuickSpriteEngine::spriteDuration(int sprite) const //Full duration, not per frame
{
    if (!m_duration[sprite] || !m_loaded)
        return m_duration[sprite];
    int state = m_things[sprite];
    if (!m_sprites[state]->m_generatedCount)
        return m_duration[sprite];
    int rowDuration;
    int extra = pseudospriteProgress(sprite, state, &rowDuration);
    if (m_sprites[state]->reverse())
        extra = (m_sprites[state]->m_generatedCount - 1) - extra;

    if (extra == m_sprites[state]->m_generatedCount - 1) {//last state
        const int durationRemaining = m_duration[sprite] % rowDuration;
        if (durationRemaining > 0)
            return durationRemaining;
    }
    return rowDuration;
}

int QQuickSpriteEngine::spriteY(int sprite) const
{
    if (!m_loaded)
        return 0;
    int state = m_things[sprite];
    if (!m_sprites[state]->m_generatedCount)
        return m_sprites[state]->m_rowY;

    int extra;
    if (m_sprites[state]->frameSync())
        extra = m_startTimes[sprite];
    else if (!m_duration[sprite])
        return m_sprites[state]->m_rowY;
    else
        extra = pseudospriteProgress(sprite, state);
    if (m_sprites[state]->reverse())
        extra = (m_sprites[state]->m_generatedCount - 1) - extra;


    return m_sprites[state]->m_rowY + m_sprites[state]->m_frameHeight * extra;
}

int QQuickSpriteEngine::spriteX(int sprite) const
{
    if (!m_loaded)
        return 0;
    int state = m_things[sprite];
    if (!m_sprites[state]->m_generatedCount)
        return m_sprites[state]->m_rowStartX;

    int extra;
    if (m_sprites[state]->frameSync())
        extra = m_startTimes[sprite];
    else if (!m_duration[sprite])
        return m_sprites[state]->m_rowStartX;
    else
        extra = pseudospriteProgress(sprite, state);
    if (m_sprites[state]->reverse())
        extra = (m_sprites[state]->m_generatedCount - 1) - extra;

    if (extra)
        return 0;
    return m_sprites[state]->m_rowStartX;
}

QQuickSprite* QQuickSpriteEngine::sprite(int sprite) const
{
    return m_sprites[m_things[sprite]];
}

int QQuickSpriteEngine::spriteWidth(int sprite) const
{
    int state = m_things[sprite];
    return m_sprites[state]->m_frameWidth;
}

int QQuickSpriteEngine::spriteHeight(int sprite) const
{
    int state = m_things[sprite];
    return m_sprites[state]->m_frameHeight;
}

int QQuickSpriteEngine::spriteCount() const //TODO: Actually image state count, need to rename these things to make sense together
{
    return m_imageStateCount;
}

void QQuickStochasticEngine::setGoal(int state, int sprite, bool jump)
{
    if (sprite >= m_things.count() || state >= m_states.count()
            || sprite < 0 || state < 0)
        return;
    if (!jump){
        m_goals[sprite] = state;
        return;
    }

    if (m_things.at(sprite) == state)
        return;//Already there
    m_things[sprite] = state;
    m_duration[sprite] = m_states.at(state)->variedDuration();
    m_goals[sprite] = -1;
    restart(sprite);
    emit stateChanged(sprite);
    emit m_states.at(state)->entered();
    return;
}

QQuickPixmap::Status QQuickSpriteEngine::status() const //Composed status of all Sprites
{
    if (!m_startedImageAssembly)
        return QQuickPixmap::Null;
    int null, loading, ready;
    null = loading = ready = 0;
    for (QQuickSprite* s : m_sprites) {
        switch (s->m_pix.status()) {
            // ### Maybe add an error message here, because this null shouldn't be reached but when it does, the image fails without an error message.
            case QQuickPixmap::Null : null++; break;
            case QQuickPixmap::Loading : loading++; break;
            case QQuickPixmap::Error : return QQuickPixmap::Error;
            case QQuickPixmap::Ready : ready++; break;
        }
    }
    if (null)
        return QQuickPixmap::Null;
    if (loading)
        return QQuickPixmap::Loading;
    if (ready)
        return QQuickPixmap::Ready;
    return QQuickPixmap::Null;
}

void QQuickSpriteEngine::startAssemblingImage()
{
    if (m_startedImageAssembly)
        return;
    m_loaded = false;
    m_errorsPrinted = false;

    //This could also trigger the start of the image loading in Sprites, however that currently happens in Sprite::setSource

    QList<QQuickStochasticState*> removals;

    for (QQuickStochasticState* s : qAsConst(m_states)) {
        QQuickSprite* sprite = qobject_cast<QQuickSprite*>(s);
        if (sprite) {
            m_sprites << sprite;
        } else {
            removals << s;
            qDebug() << "Error: Non-sprite in QQuickSpriteEngine";
        }
    }
    for (QQuickStochasticState* s : qAsConst(removals))
        m_states.removeAll(s);
    m_startedImageAssembly = true;
}

QImage QQuickSpriteEngine::assembledImage(int maxSize)
{
    QQuickPixmap::Status stat = status();
    if (!m_errorsPrinted && stat == QQuickPixmap::Error) {
        for (QQuickSprite* s : qAsConst(m_sprites))
            if (s->m_pix.isError())
                qmlWarning(s) << s->m_pix.error();
        m_errorsPrinted = true;
    }

    if (stat != QQuickPixmap::Ready)
        return QImage();

    int h = 0;
    int w = 0;
    m_maxFrames = 0;
    m_imageStateCount = 0;

    for (QQuickSprite* state : qAsConst(m_sprites)) {
        if (state->frames() > m_maxFrames)
            m_maxFrames = state->frames();

        QImage img = state->m_pix.image();

        {
            const QSize frameSize(state->m_frameWidth, state->m_frameHeight);
            if (!(img.size() - frameSize).isValid()) {
                qmlWarning(state).nospace() << "SpriteEngine: Invalid frame size " << frameSize << "."
                                            " It's bigger than image size " << img.size() << ".";
                return QImage();
            }
        }

        //Check that the frame sizes are the same within one sprite
        if (!state->m_frameWidth)
            state->m_frameWidth = img.width() / state->frames();

        if (!state->m_frameHeight)
            state->m_frameHeight = img.height();

        if (state->frames() * state->frameWidth() > maxSize){
            struct helper{
                static int divRoundUp(int a, int b){return (a+b-1)/b;}
            };
            int rowsNeeded = helper::divRoundUp(state->frames(), (maxSize / state->frameWidth()));
            if (h + rowsNeeded * state->frameHeight() > maxSize){
                if (rowsNeeded * state->frameHeight() > maxSize)
                    qmlWarning(state) << "SpriteEngine: Animation too large to fit in one texture:" << state->source().toLocalFile();
                else
                    qmlWarning(state) << "SpriteEngine: Animations too large to fit in one texture, pushed over the edge by:" << state->source().toLocalFile();
                qmlWarning(state) << "SpriteEngine: Your texture max size today is " << maxSize;
                return QImage();
            }
            state->m_generatedCount = rowsNeeded;
            h += state->frameHeight() * rowsNeeded;
            w = qMax(w, ((int)(maxSize / state->frameWidth())) * state->frameWidth());
            m_imageStateCount += rowsNeeded;
        }else{
            h += state->frameHeight();
            w = qMax(w, state->frameWidth() * state->frames());
            m_imageStateCount++;
        }
    }

    //maxFrames is max number in a line of the texture
    QImage image(w, h, QImage::Format_ARGB32_Premultiplied);
    image.fill(0);
    QPainter p(&image);
    int y = 0;
    for (QQuickSprite* state : qAsConst(m_sprites)) {
        QImage img(state->m_pix.image());
        int frameWidth = state->m_frameWidth;
        int frameHeight = state->m_frameHeight;
        if (img.height() == frameHeight && img.width() <  maxSize){//Simple case
            p.drawImage(0,y,img.copy(state->m_frameX,0,state->m_frames * frameWidth, frameHeight));
            state->m_rowStartX = 0;
            state->m_rowY = y;
            y += frameHeight;
        }else{//Chopping up image case
            state->m_framesPerRow = image.width()/frameWidth;
            state->m_rowY = y;
            int x = 0;
            int curX = state->m_frameX;
            int curY = state->m_frameY;
            int framesLeft = state->frames();
            while (framesLeft > 0){
                if (image.width() - x + curX <= img.width()){//finish a row in image (dest)
                    int copied = image.width() - x;
                    framesLeft -= copied/frameWidth;
                    p.drawImage(x,y,img.copy(curX,curY,copied,frameHeight));
                    y += frameHeight;
                    curX += copied;
                    x = 0;
                    if (curX == img.width()){
                        curX = 0;
                        curY += frameHeight;
                    }
                }else{//finish a row in img (src)
                    int copied = img.width() - curX;
                    framesLeft -= copied/frameWidth;
                    p.drawImage(x,y,img.copy(curX,curY,copied,frameHeight));
                    curY += frameHeight;
                    x += copied;
                    curX = 0;
                }
            }
            if (x)
                y += frameHeight;
        }
    }

    if (image.height() > maxSize){
        qWarning() << "SpriteEngine: Too many animations to fit in one texture...";
        qWarning() << "SpriteEngine: Your texture max size today is " << maxSize;
        return QImage();
    }

#ifdef SPRITE_IMAGE_DEBUG
    QString fPath = QDir::tempPath() + "/SpriteImage.%1.png";
    int acc = 0;
    while (QFile::exists(fPath.arg(acc))) acc++;
    image.save(fPath.arg(acc), "PNG");
    qDebug() << "Assembled image output to: " << fPath.arg(acc);
#endif

    m_loaded = true;
    m_startedImageAssembly = false;