Image.sourceだけじゃない!Qt QMLで画像を扱う多様なプログラミング手法

2025-05-31

Qtプログラミングにおける Image.source は、主にQMLで画像を扱う際に使用される非常に重要なプロパティです。簡単に言うと、表示したい画像のファイルパスやURLを指定するためのものです。

もう少し詳しく説明します。

Image.source とは

QMLの Image タイプは、ユーザーインターフェースに画像を表示するための要素です。この Image 要素がどの画像を表示すべきかを指定するのが source プロパティです。

例えば、QMLコードで以下のように使われます。

Image {
    source: "images/my_image.png" // ローカルの画像ファイルを指定
    width: 200
    height: 150
}

Image {
    source: "https://example.com/logo.svg" // ウェブ上の画像をURLで指定
    width: 100
    height: 100
}

主な機能と特徴

  • リソースパスのサポート: Qtリソースシステムに埋め込まれた画像も、qrc:///path/to/image.png のような形式で指定できます。これは、アプリケーションと一緒に画像を配布する際に非常に便利です。
  • プロパティの変更による動的な画像切り替え: source プロパティの値を変更することで、実行時に表示する画像を簡単に切り替えることができます。これは、アニメーションや状態の変化に応じて画像を変更する際に便利です。
  • キャッシュ: Qtは読み込んだ画像を自動的にキャッシュします。同じ画像を複数回表示する場合でも、再度読み込む必要がなく、パフォーマンスが向上します。
  • 非同期読み込み: 画像の読み込みは非同期で行われます。これにより、画像が読み込まれている間もUIがフリーズすることなく、スムーズなユーザーエクスペリエンスを提供します。
  • 画像の指定: ローカルファイルシステム上の画像(例: file:///path/to/image.jpg または相対パス)や、ウェブ上の画像(例: http://example.com/image.png)をURL形式で指定できます。Qtがサポートする一般的な画像フォーマット(PNG, JPEG, SVGなど)に対応しています。
  • エラー処理: 画像が見つからない場合や読み込みに失敗した場合、Image 要素は何も表示しません。status プロパティや onStatusChanged シグナルを使って、読み込みの成功・失敗をハンドリングすることができます。

    Image {
        source: "non_existent_image.png"
        onStatusChanged: {
            if (status === Image.Error) {
                console.log("画像の読み込みに失敗しました:", source);
            }
        }
    }
    
  • ネットワーク上の画像:

    Image {
        source: "https://www.qt.io/images/qt_logo.svg"
    }
    

    インターネット上の画像を読み込む場合は、ネットワーク接続が必要です。

  • ローカルファイル:

    Image {
        source: "qrc:/icons/home.svg" // Qtリソースファイル内のアイコン
    }
    

    アプリケーションに画像をバンドルする場合、qrc: プレフィックスを使用するのが一般的です。



画像ファイルが見つからない (Cannot open / QML Image: Cannot open)

これは最もよくあるエラーです。指定したパスに画像ファイルが存在しない場合に発生します。

考えられる原因とトラブルシューティング

  • ファイルのパーミッション:

    • ファイルに読み取り権限がない場合、アクセスできません。特にLinuxやmacOSで発生することがあります。
  • Qt Resource System (QRC) の設定ミス:

    • QRCファイルへの追加忘れ: アプリケーションに画像をバンドルするためにQRCファイルを使用する場合、その画像がQRCファイルに正しく追加され、ビルド設定でQRCファイルがコンパイルされているか確認します。
      • Qt Creatorの場合: .qrc ファイルを右クリックし、「Add Existing Files...」で画像を追加します。
      • CMake/QMakeの場合: ビルドシステムがQRCファイルを処理するよう設定されていることを確認します(例: CMakeLists.txt に qt_add_qml_moduleqt_add_executable にQRCファイルを含める)。
    • QRCパスの誤り: QRCファイル内でエイリアスを設定している場合、そのエイリアスパスが source に指定されているか確認します。
      • : QRCファイル内で images/my_image.png/icons/my_image.png としてエイリアスしている場合、QMLでは source: "qrc:/icons/my_image.png" と指定します。
  • パスの誤り:

    • 相対パスの理解不足: QMLファイルが実行されるディレクトリからの相対パスで指定しているかを確認します。開発環境(Qt Creatorなど)で実行する場合と、ビルドされたアプリケーションを実行する場合で、現在の作業ディレクトリが異なることがあります。
      • : Image { source: "images/my_image.png" } の場合、qmlファイルがあるディレクトリ/images/my_image.png となります。
    • 絶対パスの誤り: file:///C:/Users/user/Pictures/image.jpg のように絶対パスを使用する場合、OSのファイルシステムと一致しているか確認します。特にWindowsではバックスラッシュ(\)ではなくスラッシュ(/)を使用し、file:/// プレフィックスを付ける必要があります。
    • 大文字・小文字の区別: ファイルシステムによっては大文字・小文字を区別します(Linuxなど)。ファイル名やディレクトリ名の大文字・小文字が正確に一致しているか確認してください。

トラブルシューティングのヒント

  • 外部ビューアで開けるか確認: 問題の画像ファイルが、通常の画像ビューア(Windowsのフォト、macOSのプレビューなど)で開けるか確認します。開けない場合はファイル自体が破損している可能性があります。
  • シンプルなパスで試す: まずはプロジェクトルートやQRCの直下など、非常にシンプルなパスに画像を置いて試すことで、パスの問題かどうかの切り分けができます。
  • コンソール出力の確認: アプリケーションを実行する際に、Qtが提供するログや警告メッセージを注意深く確認します。パスの誤りや読み込み失敗に関するメッセージが表示されることが多いです。
  • Image.status プロパティの確認: Image 要素には status プロパティがあり、画像の読み込み状態を確認できます。Image.Loading (読み込み中), Image.Ready (読み込み完了), Image.Error (エラー) のいずれかです。
    Image {
        source: "non_existent_image.png"
        onStatusChanged: {
            if (status === Image.Error) {
                console.log("画像の読み込みに失敗しました:", source, "エラー:", errorString);
            } else if (status === Image.Ready) {
                console.log("画像が正常に読み込まれました:", source);
            }
        }
    }
    
    errorString プロパティも確認すると、より具体的なエラーメッセージが得られることがあります。

画像が表示されないがエラーも出ない

これはより厄介な問題です。Image.statusReady になるのに画像が見えない、または何も表示されない場合があります。

考えられる原因とトラブルシューティング

  • グラフィックドライバの問題:
    • 非常に稀ですが、グラフィックドライバやOpenGLの設定に問題がある場合、画像レンダリングに影響が出ることがあります。特に組み込みシステムで発生しやすいです。
  • 画像のフォーマットがサポートされていない:
    • Qtは主要な画像フォーマット(PNG, JPEG, SVGなど)をサポートしていますが、まれに特殊な圧縮形式や破損したファイルなど、サポートされていない形式である可能性があります。別の画像フォーマット(例: PNGからJPGに変換)を試してみてください。
    • SVGの場合、複雑なSVGファイルや特定の機能(スクリプトなど)はQMLの Image では完全にサポートされない場合があります。
  • Zオーダー(重なり順):
    • 他のQML要素の裏に画像が隠れてしまっている可能性があります。z プロパティや parent との子要素の宣言順を確認します。より後に宣言された要素ほど手前に描画されます。
  • fillMode の設定:
    • Image { fillMode: Image.PreserveAspectCrop } のように fillMode を設定している場合、コンポーネントのサイズと画像の縦横比によって画像の一部が切り取られたり、全体が収まらなかったりすることがあります。
    • widthheight を明示的に指定しない場合、画像の元のサイズが使われます。
  • 画像のサイズが0または小さすぎる:
    • widthheight が設定されていない、または 0 になっている場合、画像は表示されません。
    • 画像自体のサイズが非常に小さい場合、表示されていても見えないことがあります。

トラブルシューティングのヒント

  • 画像自体を一時的に変更する: 非常にシンプルな、確実に表示されると分かっている画像(例: Qtのロゴなど)に source を一時的に変更して、Image 要素自体が機能しているかを確認します。
  • 背景色を設定する: Image 要素の親要素に分かりやすい背景色(例: Rectangle { color: "red"; Image { ... } })を設定し、画像が表示されるべき領域が適切に確保されているか確認します。
  • Image.statusImage.paintedWidth, Image.paintedHeight の確認:
    Image {
        source: "path/to/image.png"
        onStatusChanged: {
            if (status === Image.Ready) {
                console.log("画像サイズ:", paintedWidth, "x", paintedHeight);
            }
        }
    }
    
    paintedWidthpaintedHeight で、実際に描画されているサイズを確認します。

動的に source を変更した際の問題

source プロパティをJavaScriptやC++から動的に変更する場合に発生する問題です。

考えられる原因とトラブルシューティング

  • キャッシュの問題:
    • Qtは画像をキャッシュするため、同じURLの画像でも内容が更新されている場合に古い画像が表示されることがあります。これを防ぐには、URLにタイムスタンプやランダムなクエリパラメータを追加して、常に新しいURLとして認識させる方法があります。
    • 例: source: "http://example.com/image.png?" + Date.now()
  • ファイルの読み込みタイミング:
    • ネットワーク経由の画像を頻繁に切り替える場合、ネットワークの遅延により画像が表示されるまでに時間がかかったり、古い画像が表示されたままになったりすることがあります。
    • Image.asynchronous: true はデフォルトですが、これにより読み込みがバックグラウンドで行われます。同期的に読み込みたい場合は false に設定できますが、UIがフリーズする可能性があるため非推奨です。
  • パスの更新ミス:
    • 新しいパスが正しく生成されていない、または古いパスが残っている可能性があります。

トラブルシューティングのヒント

  • Image.status で読み込み完了を待つ:
    Image {
        id: dynamicImage
        source: "" // 初期値
        onStatusChanged: {
            if (status === Image.Ready) {
                console.log("新しい画像が読み込まれました:", source);
            } else if (status === Image.Error) {
                console.log("画像読み込みエラー:", source, errorString);
            }
        }
    }
    
    // JavaScriptなどから
    dynamicImage.source = "new_image.png";
    
    読み込みが完了したことを onStatusChanged で確認することで、デバッグがしやすくなります。
  • console.log でパスを確認: source を変更する直前と直後に、実際に設定されているパスを console.log() で出力し、期待通りのパスになっているか確認します。
  • ビルド環境と実行環境の差異: 開発マシンでは動くが、ターゲットデバイス(組み込みLinuxなど)では動かない場合、ファイルパスやパーミッション、あるいはQtの画像プラグイン(例: JPEGやPNGを扱うための qjpeg.so, qpng.so など)がターゲット環境に適切にデプロイされているか確認する必要があります。特にデプロイメントの際に、必要なプラグインが抜け落ちることがよくあります。
  • OS固有のパスの問題: WindowsとLinux/macOSではパスの記述方法(\ vs /)や絶対パスの形式が異なります。クロスプラットフォーム開発では、QUrl::fromLocalFile() (C++) や Qt.resolvedUrl() (QML) を適切に使用してパスを構築することが重要です。


基本的な画像表示 (ローカルファイル)

最も基本的な例です。QMLファイルと同じディレクトリ、またはそのサブディレクトリにある画像を読み込みます。

main.qml

import QtQuick 2.15
import QtQuick.Window 2.15

Window {
    width: 640
    height: 480
    visible: true
    title: "Basic Image Example"

    // 'images' フォルダ内の 'qt_logo.png' を表示
    // 実際には、main.qml と同じ階層に 'images' フォルダがあり、その中に画像があることを想定
    Image {
        source: "images/qt_logo.png"
        anchors.centerIn: parent // ウィンドウの中央に配置
        width: 200
        height: 200
        // fillMode: Image.PreserveAspectFit // アスペクト比を保ちつつ、指定された領域に収まるように調整
    }
}

解説

  • この例を動かすには、プロジェクトのルートディレクトリに images フォルダを作成し、その中に qt_logo.png という名前の画像を配置してください。
  • source: "images/qt_logo.png" で、main.qml がある場所からの相対パスを指定しています。

Qt Resource System (QRC) からの画像表示

アプリケーションに画像をバンドルする際に最も推奨される方法です。実行ファイル内に画像が埋め込まれるため、配布が容易になり、パスの問題も減ります。

myresources.qrc (Qt Creatorで作成)

<!DOCTYPE RCC><RCC version="1.0">
<qresource prefix="/icons">
    <file>home.svg</file>
</qresource>
<qresource prefix="/images">
    <file>background.jpg</file>
    <file alias="logo">qt_logo.png</file> </qresource>
</RCC>

main.qml

import QtQuick 2.15
import QtQuick.Window 2.15

Window {
    width: 640
    height: 480
    visible: true
    title: "QRC Image Example"

    // QRCファイルの '/icons' プレフィックス内の 'home.svg' を表示
    Image {
        id: homeIcon
        source: "qrc:/icons/home.svg"
        anchors.left: parent.left
        anchors.top: parent.top
        width: 64
        height: 64
        fillMode: Image.PreserveAspectFit
    }

    // QRCファイルの '/images' プレフィックス内の 'background.jpg' を背景として表示
    Image {
        source: "qrc:/images/background.jpg"
        anchors.fill: parent
        fillMode: Image.PreserveAspectFill // 領域全体を覆うように拡大(はみ出す部分は切り取られる)
        // z: -1 // 他の要素の背後に配置
    }

    // QRCファイル内でエイリアス 'logo' を使って 'qt_logo.png' を表示
    Image {
        source: "qrc:/images/logo" // エイリアスでアクセス
        anchors.centerIn: parent
        width: 150
        height: 150
    }
}

解説

  • myresources.qrc ファイルをプロジェクトに追加し、Qt Creator の .pro ファイルや CMakeLists.txt に適切に含める必要があります。
    • .pro の場合: RESOURCES += myresources.qrc
    • CMakeLists.txt の場合: qt_add_qml_module(... RESOURCES myresources.qrc ...) または qt_add_executable(... myresources.qrc ...)
  • qrc:/ プレフィックスを使って、Qt Resource System内のパスを指定します。

ウェブ上の画像を表示 (URL)

インターネット上の画像を直接読み込むことができます。

main.qml

import QtQuick 2.15
import QtQuick.Window 2.15

Window {
    width: 640
    height: 480
    visible: true
    title: "Web Image Example"

    Image {
        id: webImage
        source: "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e0/Qt_logo_2016.svg/100px-Qt_logo_2016.svg.png" // Qtのロゴ画像(例)
        anchors.centerIn: parent
        width: 200
        height: 200
        fillMode: Image.PreserveAspectFit

        // 画像の読み込み状態を監視
        onStatusChanged: {
            if (webImage.status === Image.Loading) {
                console.log("画像読み込み中...");
            } else if (webImage.status === Image.Ready) {
                console.log("画像の読み込みが完了しました!");
            } else if (webImage.status === Image.Error) {
                console.log("画像の読み込みに失敗しました:", webImage.errorString);
            }
        }
    }
}

解説

  • onStatusChanged シグナルを使って、画像の読み込み状況(Loading, Ready, Error)を監視できます。ネットワークの状況によっては読み込みに時間がかかったり、失敗したりすることがあるため、この監視は重要です。
  • source に完全なURLを指定します。

JavaScript を使った動的な画像切り替え

ボタンクリックなどのイベントに応じて画像を変更する例です。

main.qml

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15

Window {
    width: 640
    height: 480
    visible: true
    title: "Dynamic Image Change"

    property int currentImageIndex: 0
    property var imageSources: [
        "qrc:/images/image1.jpg",
        "qrc:/images/image2.jpg",
        "qrc:/images/image3.jpg"
    ]

    Image {
        id: dynamicImage
        source: imageSources[currentImageIndex] // 初期画像
        anchors.centerIn: parent
        width: 300
        height: 200
        fillMode: Image.PreserveAspectFit
        onStatusChanged: {
            if (status === Image.Error) {
                console.log("エラー: 画像の読み込みに失敗しました - ", source, errorString);
            }
        }
    }

    Button {
        text: "Next Image"
        anchors.horizontalCenter: parent.horizontalCenter
        anchors.top: dynamicImage.bottom
        anchors.topMargin: 20

        onClicked: {
            currentImageIndex = (currentImageIndex + 1) % imageSources.length;
            dynamicImage.source = imageSources[currentImageIndex]; // sourceプロパティを更新
            console.log("画像を変更しました:", dynamicImage.source);
        }
    }
}

解説

  • この例を動かすには、QRCファイルに image1.jpg, image2.jpg, image3.jpg を追加する必要があります。
  • ButtononClicked シグナルで currentImageIndex を更新し、その値を使って dynamicImage.source を設定し直しています。QMLのプロパティバインディングにより、dynamicImage.source が自動的に更新されます。
  • imageSources というリストに画像のパスを定義し、currentImageIndex で現在の画像を管理しています。

大きな画像をロードする際、表示サイズが小さい場合でも元画像をフルサイズで読み込むとメモリを無駄に消費します。sourceSize を使うと、指定されたサイズに画像を縮小してロードできます。

main.qml

import QtQuick 2.15
import QtQuick.Window 2.15

Window {
    width: 640
    height: 480
    visible: true
    title: "Source Size Example"

    Image {
        source: "qrc:/images/large_image.jpg" // 非常に大きな画像ファイル
        width: 150
        height: 150
        anchors.centerIn: parent
        fillMode: Image.PreserveAspectFit

        // 画像をロードする際に、このサイズに縮小してからメモリに読み込む
        // これにより、大きな画像を扱う際のメモリ使用量を削減できる
        sourceSize.width: width * 2 // 例えば、表示サイズの2倍の解像度でロード
        sourceSize.height: height * 2 // Retinaディスプレイなどで考慮
        // あるいは固定値: sourceSize.width: 300; sourceSize.height: 300;
    }
}
  • この例を動かすには、QRCファイルに大きな画像ファイル(例: large_image.jpg)を追加してください。
  • widthheight は、QML要素として最終的に表示されるサイズです。sourceSize は、その表示のために実際にロードされる画像の最大サイズを指定します。
  • sourceSize プロパティは、画像がメモリにロードされる際の目標サイズを指定します。これにより、不要な大きな解像度の画像データがメモリを圧迫するのを防ぎます。


ImageProvider を使用する (C++ と QML の連携)

これは最も強力で柔軟な代替手段の一つです。ImageProvider を使うと、C++ コードで生成した画像データ(例: メモリ上のピクセルデータ、データベースから読み込んだ画像、リアルタイムで生成されるグラフなど)を QML の Image 要素に渡すことができます。

使用ケース

  • 画像をロードする前にC++側で前処理(例: ウォーターマークの追加、サイズ変更)を施したい場合。
  • カスタムの画像フォーマットをサポートしたい場合。
  • リアルタイムで画像を生成し(例: カメラからのプレビュー、描画キャンバスの内容)、QMLで表示したい場合。
  • データベースから画像を読み込んで表示したい場合。

基本的な流れ

  1. QQuickImageProvider を継承したC++クラスを作成します。
  2. requestImage() または requestPixmap() メソッドをオーバーライドし、画像データを返します。
  3. QMLエンジンにこの ImageProvider を登録します。
  4. QMLの Image.source で、image://<provider_id>/<image_id> の形式で指定します。

C++ コード (myimageprovider.h)

#ifndef MYIMAGEPROVIDER_H
#define MYIMAGEPROVIDER_H

#include <QQuickImageProvider>
#include <QImage>

class MyImageProvider : public QQuickImageProvider
{
public:
    MyImageProvider()
        : QQuickImageProvider(QQuickImageProvider::Image) // または Pixmap
    {}

    QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize) override
    {
        qDebug() << "Requesting image with ID:" << id;
        QImage image(requestedSize.width() > 0 ? requestedSize.width() : 100,
                     requestedSize.height() > 0 ? requestedSize.height() : 100,
                     QImage::Format_ARGB32);
        image.fill(Qt::blue); // 例として青い画像
        if (id == "myCustomImage") {
            // IDに基づいて異なる画像を生成
            image.fill(Qt::green);
        } else if (id == "dynamicRect") {
            image.fill(Qt::red);
            QPainter painter(&image);
            painter.setPen(Qt::black);
            painter.drawRect(10, 10, image.width() - 20, image.height() - 20);
        }

        if (size)
            *size = image.size(); // 実際の画像サイズを返す

        return image;
    }
};

#endif // MYIMAGEPROVIDER_H

C++ コード (main.cpp)

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "myimageprovider.h" // 作成したプロバイダをインクルード

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    // ImageProvider を "myprovider" というIDで登録
    engine.addImageProvider(QStringLiteral("myprovider"), new MyImageProvider);

    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);

    return app.exec();
}

QML コード (main.qml)

import QtQuick 2.15
import QtQuick.Window 2.15

Window {
    width: 640
    height: 480
    visible: true
    title: "ImageProvider Example"

    Column {
        anchors.centerIn: parent
        spacing: 20

        Image {
            source: "image://myprovider/myCustomImage" // プロバイダIDと画像IDを指定
            width: 150
            height: 150
            fillMode: Image.PreserveAspectFit
        }

        Image {
            source: "image://myprovider/dynamicRect"
            width: 200
            height: 100
            fillMode: Image.PreserveAspectFit
        }
    }
}

Canvas を使用してピクセルデータを描画する

Canvas QMLタイプは、JavaScriptまたはC++の QPainter を使用して、ピクセルレベルでカスタムなグラフィックを描画するための強力なツールです。画像をロードするのではなく、自分で描画したい場合に適しています。

使用ケース

  • 画像をロードするのではなく、特定のアルゴリズムに基づいてピクセルデータを生成したい場合。
  • プログラムで完全に生成される幾何学的な図形やテクスチャ。
  • ユーザーが自由にお絵かきできるキャンバス。
  • リアルタイムで変化するグラフやチャート。

QML コード (main.qml)

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15

Window {
    width: 640
    height: 480
    visible: true
    title: "Canvas Example"

    Canvas {
        id: myCanvas
        anchors.fill: parent
        onPaint: {
            var ctx = getContext("2d"); // 2D描画コンテキストを取得
            ctx.clearRect(0, 0, width, height); // キャンバスをクリア

            // 円を描画
            ctx.beginPath();
            ctx.arc(width / 2, height / 2, 50, 0, 2 * Math.PI, false);
            ctx.fillStyle = "blue";
            ctx.fill();
            ctx.lineWidth = 5;
            ctx.strokeStyle = "black";
            ctx.stroke();

            // テキストを描画
            ctx.font = "20px Arial";
            ctx.fillStyle = "red";
            ctx.fillText("Hello Canvas!", 10, 30);
        }

        // キャンバスを再描画するトリガー
        MouseArea {
            anchors.fill: parent
            onClicked: {
                myCanvas.requestPaint(); // クリックするたびに再描画
            }
        }
    }

    Button {
        text: "Update Canvas"
        anchors.bottom: parent.bottom
        anchors.horizontalCenter: parent.horizontalCenter
        onClicked: {
            myCanvas.requestPaint(); // ボタンクリックで再描画
        }
    }
}

解説

  • Canvas は画像の読み込み機能はありませんが、既存の画像を drawImage() メソッドで描画することもできます(Image.source のような直接的な参照ではない)。
  • requestPaint() を呼び出すことで、キャンバスの描画をトリガーできます。
  • onPaint ハンドラ内で、JavaScriptのCanvas APIに似た描画コマンドを使って図形やテキストを描画します。

ShaderEffect を使用する

ShaderEffect は、OpenGL ES Shading Language (GLSL) を使って、GPU上でカスタムなグラフィックエフェクトやテクスチャを生成・操作するための高度な方法です。画像そのものを生成する用途よりも、既存の画像にエフェクトを適用したり、プロシージャルなテクスチャを生成したりするのに使われます。

使用ケース

  • パフォーマンスが要求される画像操作。
  • 複雑なノイズパターンやフラクタル図形などのプロシージャルなテクスチャ生成。
  • リアルタイムの画像フィルター(ぼかし、色調補正など)。

QML コード (例: シンプルな色反転シェーダー)

import QtQuick 2.15
import QtQuick.Window 2.15

Window {
    width: 640
    height: 480
    visible: true
    title: "ShaderEffect Example"

    Rectangle {
        anchors.fill: parent
        color: "lightgray"

        // 元の画像
        Image {
            id: originalImage
            source: "qrc:/images/sample_image.png"
            width: 200
            height: 200
            anchors.centerIn: parent
            anchors.verticalCenterOffset: -100
        }

        // シェーダーを適用する四角形
        ShaderEffect {
            id: colorInvertEffect
            width: originalImage.width
            height: originalImage.height
            anchors.centerIn: parent
            anchors.verticalCenterOffset: 100

            // シェーダーに渡す入力テクスチャ (元の画像)
            property variant source: originalImage
            // property variant customColor: "red" // シェーダーにカスタム値を渡すことも可能

            // フラグメントシェーダーコード (GLSL)
            fragmentShader: "
                varying highp vec2 qt_UV; // Qtが提供するテクスチャ座標
                uniform highp sampler2D source; // 入力テクスチャ

                void main() {
                    // テクスチャからピクセルを取得
                    highp vec4 color = texture2D(source, qt_UV);
                    // 色を反転
                    gl_FragColor = vec4(1.0 - color.r, 1.0 - color.g, 1.0 - color.b, color.a);
                }
            "
        }
    }
}

解説

  • これは高度なトピックであり、GLSLの知識が必要です。
  • fragmentShader にGLSLコードを直接記述し、ピクセル単位での操作を定義します。
  • ShaderEffect は、source プロパティを使って他の Item(この場合は Image)のレンダリング結果を入力テクスチャとして受け取ることができます。

これは ImageProvider に似ていますが、より直接的に QVariant 経由で QImageQPixmap をQMLに渡す方法です。特に、C++側で一時的に画像を生成・操作し、それを一度だけQMLに表示したい場合に便利です。

C++ コード (main.cpp)

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext> // QQmlContext をインクルード
#include <QImage>
#include <QPainter>

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;

    // C++ で QImage を生成
    QImage generatedImage(200, 200, QImage::Format_ARGB32);
    generatedImage.fill(Qt::yellow);
    QPainter painter(&generatedImage);
    painter.setPen(QPen(Qt::black, 5));
    painter.drawLine(0, 0, 200, 200);
    painter.drawLine(0, 200, 200, 0);
    painter.end();

    // QMLコンテキストに QVariant 経由で QImage を公開
    engine.rootContext()->setContextProperty(QStringLiteral("myGeneratedImage"),
                                            QVariant::fromValue(generatedImage));

    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);

    return app.exec();
}

QML コード (main.qml)

import QtQuick 2.15
import QtQuick.Window 2.15

Window {
    width: 640
    height: 480
    visible: true
    title: "C++ Image to QML"

    Image {
        // C++から公開された QImage を直接 source に設定
        // QML側では Image.source の値として QVariant に変換された QImage が認識される
        source: myGeneratedImage
        anchors.centerIn: parent
        width: 250 // 表示サイズは QML で指定
        height: 250
        fillMode: Image.PreserveAspectFit
    }
}
  • この方法は、動的に頻繁に画像を更新する場合には ImageProvider の方がパフォーマンスが良いことが多いです。
  • QML側では、Image.source に直接そのコンテキストプロパティ名を指定します。Qtが自動的に QVariant から画像データを抽出し、表示します。
  • QQmlContext::setContextProperty() を使って、C++の QImage または QPixmap オブジェクトを QVariant にラップしてQMLに公開します。