LoginSignup
5
1

More than 1 year has passed since last update.

Qt で Quite OK Image を表示する

Last updated at Posted at 2021-12-02

はじめに

この記事は Qt Advent Calendar 2021 3日目の記事です。

昨日は、@phyblas さんの PyQtのドラッグ&ドロップの使い方 という大作でした。様々な形で使うことができるんですね。

今日は QOI について書きたいと思います。

Quite OK Image とは

11/24 に発表された、なぜか速い画像の可逆圧縮コードです。

日本語はこちらがわかりやすいです。

Qt で対応している画像の形式

Qt では標準で以下の画像の形式に対応しています。

Format Description Qt's support
BMP Windows Bitmap Read/write
GIF Graphic Interchange Format (optional) Read
JPG Joint Photographic Experts Group Read/write
JPEG Joint Photographic Experts Group Read/write
PNG Portable Network Graphics Read/write
PBM Portable Bitmap Read
PGM Portable Graymap Read
PPM Portable Pixmap Read/write
XBM X11 Bitmap Read/write
XPM X11 Pixmap Read/write

ドキュメント: Reading and Writing Image Files

それから、Qt SVG モジュールを利用することで、SVG 形式の画像を扱うことができます。

さらに、Qt Image Format というアドオンのモジュールを利用することで、以下の画像形式にも対応可能です。

Format Description Support 3rd Party Codec
ICNS Apple Icon Image Read/write No
JP2 Joint Photographic Experts Group 2000 Read/write Yes (Not bundled)
MNG Multiple-image Network Graphics Read Yes (Not bundled)
TGA Truevision Graphics Adapter Read No
TIFF Tagged Image File Format Read/write Yes (bundled)
WBMP Wireless Bitmap Read/write No
WEBP WebP Read/write Yes (bundled)

ドキュメント: The Image I/O Plugins

Qt で画像の形式を追加する方法

Qt では画像の形式を プラグイン で実装しており、独自の拡張も簡単になっています。

実際には QImageIOPlugin に対応したプラグインを作成する必要があります。

QtQOIImagePlugin の作成

プロジェクトファイル

QtQOIImagePlugin.pro
TEMPLATE = lib
QT = gui
CONFIG += plugin
DEFINES += QOI_NO_STDIO

SOURCES += \
    qqoiimageioplugin.cpp

HEADERS += \
    qqoiimageioplugin.h \
    qio.h

DISTFILES += QtQOIImagePlugin.json

# Default rules for deployment.
unix {
    target.path = $$[QT_INSTALL_PLUGINS]/imageformats
}
!isEmpty(target.path): INSTALLS += target

Qt Creator でプラグインを作成し、適当にアップデートしました。

JSON ファイル

QtQOIImagePlugin.json
{
    "Keys" : [ "qoi" ]
}

これもまぁテンプレですね。

ヘッダファイル

qqoiimageplugin.h
#ifndef QQOIIMAGEIOPLUGIN_H
#define QQOIIMAGEIOPLUGIN_H

#include <QtGui/QImageIOPlugin>

class QQOIImageIOPlugin : public QImageIOPlugin
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID QImageIOHandlerFactoryInterface_iid FILE "QtQOIImagePlugin.json")

public:
    explicit QQOIImageIOPlugin(QObject *parent = nullptr);

    QImageIOPlugin::Capabilities capabilities(QIODevice *device, const QByteArray &format) const override;
    QImageIOHandler *create(QIODevice *device, const QByteArray &format) const override;
};

#endif // QQOIIMAGEIOPLUGIN_H

これも、シンプルに QImageIOPlugin のサブクラスを作成し、必要なメソッドをオーバーライドしています。

qio.h

e9069e11a43d779b418679c7a50b2ec14f652085 のものを利用しました。

ソースファイル

qqoiimageplugin.cpp
#include "qqoiimageioplugin.h"

#define QOI_IMPLEMENTATION
#define QOI_MALLOC(sz) (unsigned char *)malloc(sz)
#define QOI_FREE(p)    (unsigned char *)free(p)

#include "qio.h"

#include <QtCore/QDebug>

class QQOIImageIOHandler : public QImageIOHandler
{
public:
    bool canRead() const override;
    bool read(QImage *image) override;
};

bool QQOIImageIOHandler::canRead() const
{
    if (!device())
        return false;
    return device()->bytesAvailable() > 0;
}

bool QQOIImageIOHandler::read(QImage *image)
{
    auto data = device()->readAll();
    int size = data.length();
    qoi_desc spec {};
    void *decoded = qoi_decode(data.constData(), size, &spec, 4);
    if (!decoded)
        return false;

    *image = QImage(spec.width, spec.height, QImage::Format_ARGB32);

    auto p = static_cast<unsigned char *>(decoded);
    for (unsigned int y = 0; y < spec.height; y++) {
        auto *l = reinterpret_cast<unsigned int *>(image->scanLine(y));
        for (unsigned int x = 0; x < spec.width; x++) {
            const auto r = *p++;
            const auto g = *p++;
            const auto b = *p++;
            const auto a = *p++;
            *l++ = qRgba(r, g, b, a);
        }
    }
    free(decoded);
    return true;
}

QQOIImageIOPlugin::QQOIImageIOPlugin(QObject *parent)
    : QImageIOPlugin(parent)
{}

QImageIOPlugin::Capabilities QQOIImageIOPlugin::capabilities(QIODevice *device, const QByteArray &format) const
{
    QImageIOPlugin::Capabilities ret;
    if (format == "qoi") {
        if (device) {
            if (device->isReadable())
                ret |= QImageIOPlugin::CanRead;
        } else {
            ret |= QImageIOPlugin::CanRead;
        }
    }
    return ret;
}

QImageIOHandler *QQOIImageIOPlugin::create(QIODevice *device, const QByteArray &format) const
{
    auto ret = new QQOIImageIOHandler;
    ret->setDevice(device);
    ret->setFormat(format);
    return ret;
}

特に、説明するような凝ったことはしていなくて、
QOI 向けの QImageIOHandler のサブクラスを実装し、QImagePlugin の create 関数で返しています。

実行

Qt で画像を表示するさいに、QGraphicsView を利用する方が結構多いみたいですが、
シンプルなケースでは、QLabel で十分です。

今回は Qt Quick で画像を表示してみましょう。

QtQIOImagePlugin.qml
import QtQuick
import QtQuick.Window

Window {
    width: 300
    height: 300
    visible: true
    title: qsTr("Hello QOI Image")

    Image {
        anchors.centerIn: parent
        source: 'len_std.qoi'
    }
}

image.png

おわりに

今回のソースコードは https://github.com/task-jp/qtqoiimageplugin で公開しています。
ぜひお試しください!

明日は @task-jp さんの「Qt にバグ報告をして直してもらった感じのことを書きます」です。お楽しみに!

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1