Qimages容器

时间:2018-01-28 13:03:29

标签: qml qt5 sprite

我正在寻找像QSprite这样的类,它负责从源文件中读取像素图,将源图像分割成图像序列并按索引返回子图像。

这样的事情:

class QSprite2
{
    bool load(QUrl url, QSize frameSize);
    QImage getFrame(int index);
};

有没有办法用现有的类来描述功能。或许我必须自己实现描述逻辑?

1 个答案:

答案 0 :(得分:0)

您可以使用QQuickImageProvider。这是我写的一个类似的东西:

SpriteImageProvider.h

#ifndef SPRITEIMAGEPROVIDER_H
#define SPRITEIMAGEPROVIDER_H

#include <QHash>
#include <QImage>
#include <QString>
#include <QQuickImageProvider>

#include "IsleGlobal.h"

class ISLE_EXPORT SpriteImageProvider : public QQuickImageProvider
{
public:
    SpriteImageProvider();
    QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize) override;

private:
    int parseFrameIndex(const QString &frameIndexStr) const;
    int parseFrameWidth(const QString &frameWidthStr) const;
    int parseFrameHeight(const QString &frameHeightStr) const;

    QHash<QString, QImage> mImages;
};

#endif // SPRITEIMAGEPROVIDER_H

SpriteImageProvider.cpp

#include "SpriteImageProvider.h"

#include <QImage>
#include <QDebug>

SpriteImageProvider::SpriteImageProvider() :
    QQuickImageProvider(QQmlImageProviderBase::Image)
{
}

static const QRgb whiteRgba = qRgba(255, 255, 255, 255);
static const QRgb magentaRgba = qRgba(255, 0, 255, 255);
static const QRgb transparentRgba = qRgba(0, 0, 0, 0);

QImage SpriteImageProvider::requestImage(const QString &id, QSize *size, const QSize &)
{
    QStringList args = id.split(QLatin1String(","));
    if (args.length() < 1) {
        qWarning() << "Must pass at least the file name as arguments for SpriteImageProvider source";
        return QImage();
    } else if (args.length() != 1 && args.length() != 2 && args.length() != 4) {
        qWarning() << "Must pass either (fileName), (fileName, frameIndex) or"
                   << "(fileName, frameWidth, frameHeight, frameIndex) as arguments for SpriteImageProvider source";
        return QImage();
    }

    const QString imageFilename = ":/" + args.first();

    int frameIndex = -2;
    int frameWidth = -2;
    int frameHeight = -2;

    if (args.length() > 1) {
        frameIndex = parseFrameIndex(args.last());
        if (frameIndex == -1)
            return QImage();
    }

    if (args.length() == 4) {
        frameWidth = parseFrameIndex(args.at(1));
        if (frameWidth == -1)
            return QImage();

        frameHeight = parseFrameIndex(args.at(2));
        if (frameHeight == -1)
            return QImage();
    }

    QHash<QString, QImage>::const_iterator it = mImages.find(imageFilename);
    if (it == mImages.end()) {
        QImage image(imageFilename);
        if (image.isNull()) {
            qWarning() << "Failed to load image at" << imageFilename;
            return image;
        }

        // TODO: is there a better way of doing this?
        QImage alphaImage = image;
        for (int y = 0; y < alphaImage.height(); ++y) {
            for (int x = 0; x < alphaImage.width(); ++x) {
                const QRgb pixelRgb = alphaImage.pixel(x, y);
                if (pixelRgb == whiteRgba || pixelRgb == magentaRgba)
                    alphaImage.setPixel(x, y, transparentRgba);
            }
        }

        mImages.insert(imageFilename, alphaImage);
        it = mImages.find(imageFilename);
    }

    if (frameWidth == -2 || frameHeight == -2) {
        if (frameIndex == -2) {
            // Use the whole image.
            frameWidth = it.value().width();
            frameHeight = it.value().height();
            frameIndex = 0;
        } else {
            frameWidth = 64;
            frameHeight = 64;
        }
    }

    // Copy an individual frame out of the larger image.
    const int framesWide = it.value().width() / frameWidth;
    const QRect subRect((frameIndex % framesWide) * frameWidth,
                        (frameIndex / framesWide) * frameHeight,
                        frameWidth,
                        frameHeight);
//    qDebug() << "id" << id;
//    qDebug() << "framesWide" << framesWide;
//    qDebug() << "subRect" << subRect;
    const QImage frameImage = it.value().copy(subRect);
    *size = frameImage.size();
    return frameImage;
}


int SpriteImageProvider::parseFrameIndex(const QString &frameIndexStr) const
{
    bool convertedToIntSuccessfully = false;
    const int frameIndex = frameIndexStr.toInt(&convertedToIntSuccessfully);
    if (!convertedToIntSuccessfully) {
        qWarning() << "Failed to convert frame index" << frameIndexStr << "to an int";
        return -1;
    }
    return frameIndex;
}

int SpriteImageProvider::parseFrameWidth(const QString &frameWidthStr) const
{
    bool convertedToIntSuccessfully = false;
    const int frameWidth = frameWidthStr.toInt(&convertedToIntSuccessfully);
    if (!convertedToIntSuccessfully) {
        qWarning() << "Failed to convert frame width" << frameWidthStr << "to an int";
        return -1;
    }
    return frameWidth;
}

int SpriteImageProvider::parseFrameHeight(const QString &frameHeightStr) const
{
    bool convertedToIntSuccessfully = false;
    const int frameHeight = frameHeightStr.toInt(&convertedToIntSuccessfully);
    if (!convertedToIntSuccessfully) {
        qWarning() << "Failed to convert frame height" << frameHeightStr << "to an int";
        return -1;
    }
    return frameHeight;
}

它还支持“alpha键” - 源图像中的白色和品红色像素将变为透明像素。

它已在QML引擎中注册,如下所示:

mEngine->addImageProvider("sprite", new SpriteImageProvider);

并在QML中使用:

import QtQuick 2.5

Image {
     source: qsTr('image://sprite/sprites/hugh.png,%1').arg(_sceneItemComponent ? _sceneItemComponent.facingDirection : 0)
}

其中facingDirection是要使用的帧索引。您还可以传递其他参数,这些参数在.cpp文件中提到。