QML Image type徹底解説: 画像が表示されない?サイズが崩れる?解決策と代替手法

2025-05-31



Qt/QMLアプリケーションでImageタイプを使用する際、以下のようなエラーに遭遇することがよくあります。それぞれの問題に対する一般的な原因と解決策を説明します。

画像が表示されない、またはブランクになる

よくある原因

  • 幅/高さが0
    • width または height プロパティが設定されていないか、0になっているため、表示領域がない。
  • Z-order の問題
    • 他のアイテムがImageの上に描画されていて隠れている。
  • 非同期ローディングの問題
    • ネットワーク経由や大きな画像を読み込む場合、ローディングが完了する前に表示しようとしている。
  • 画像ファイルの破損
    • 画像ファイル自体が破損しているか、サポートされていない形式である。
  • 画像ファイルが存在しない
    • 指定されたパスに画像ファイルが物理的に存在しない。
  • 不正なパス
    • ファイルパスが間違っている(スペルミス、大文字小文字の区別、相対パスの基準点の誤解)。
    • リソースシステム (.qrc ファイル) を使用している場合、プレフィックスが間違っているか、ファイルが .qrc に正しく追加されていない。

トラブルシューティング

  1. パスの確認
    • source プロパティのパスを二重に確認します。絶対パスで試して問題が解決するかどうか確認し、その後相対パスを修正します。
    • リソースシステムを使用している場合は、qrc:/prefix/path/to/image.png の形式で正しく記述されているか、Qt Creator のリソースエディタで確認します。
    • console.log("Image source:", myImage.source); のようにして、実行時にQMLからパスを出力し、正しいパスが使用されていることを確認します。
  2. ファイル存在の確認
    • ファイルエクスプローラーで画像ファイルが指定されたパスに実際に存在することを確認します。
  3. 画像形式の確認
    • QMLがサポートする画像形式(PNG, JPEG, GIFなど)であることを確認します。
    • 別の画像ファイルで試してみて、画像ファイル自体に問題がないか確認します。
  4. status プロパティの確認
    • Image アイテムの status プロパティを使用します。これは画像のロード状態を示します。
    Image {
        id: myImage
        source: "path/to/image.png"
        onStatusChanged: {
            if (myImage.status === Image.Error) {
                console.log("Image loading error:", myImage.errorString);
            } else if (myImage.status === Image.Ready) {
                console.log("Image loaded successfully!");
            }
        }
    }
    
    errorString を確認することで、具体的なエラーメッセージが得られます。
  5. implicitWidth / implicitHeight の確認
    • widthheight を明示的に設定しない場合、Imageは画像の本来のサイズを implicitWidthimplicitHeight として取得します。これらが0でないことを確認します。
  6. Z-order の調整
    • z プロパティを設定して、Imageが他のアイテムの上に表示されるように調整します。
  7. fillMode の確認
    • fillModeImage.Stretch など、適切に設定されているか確認します。Image.PreserveAspectFitImage.PreserveAspectCrop の場合、親アイテムのサイズに対して画像が小さすぎると見えにくいことがあります。

画像のサイズやアスペクト比が正しくない

よくある原因

  • 親アイテムのサイズの影響
    • anchors.fill: parent などを使用しているが、親アイテムのサイズが期待通りでない。
  • fillMode の誤解
    • fillMode プロパティが意図しない挙動をしている。
  • width と height の設定ミス
    • 画像の本来のアスペクト比を考慮せずに、widthheight を固定値で設定している。

トラブルシューティング

  1. fillMode の適切な選択
    • Image.PreserveAspectFit (デフォルト)
      アスペクト比を維持して、指定された領域に収まるように拡大縮小します。画像全体が表示されます。
    • Image.PreserveAspectCrop
      アスペクト比を維持して、指定された領域を完全に埋めるように拡大縮小します。画像の一部が切り取られる可能性があります。
    • Image.Stretch
      アスペクト比を無視して、指定された領域に合わせて引き伸ばされます。画像が歪む可能性があります。
    • Image.Tile
      画像が領域を埋めるまでタイル状に繰り返されます。
    • 目的に合わせて fillMode を選びます。
  2. width と height の連携
    • アスペクト比を維持したい場合は、片方の寸法のみを設定し、もう片方を implicitWidth / implicitHeight に基づいて計算するか、fillMode を活用します。
    // 幅を固定し、高さはアスペクト比を維持
    Image {
        width: 200
        height: implicitHeight * (width / implicitWidth) // またはfillModeを使う
        source: "image.png"
        fillMode: Image.PreserveAspectFit // これで十分なことが多い
    }
    
  3. sourceSize の使用(ロード時のスケーリング)
    • 大きな画像をロードする際に、メモリ効率を良くするために、ロード時に画像をスケーリングする sourceSize プロパティを使用できます。
    Image {
        source: "large_image.png"
        sourceSize.width: 100 // 画像をロード時に100px幅にスケーリング
        sourceSize.height: 100 // アスペクト比を維持したい場合は片方のみ設定
        fillMode: Image.PreserveAspectFit
    }
    
    これは表示サイズではなく、ロードされる画像のピクセルデータサイズに影響します。

アニメーションGIFが動かない

よくある原因

  • QML Image の制限
    • QMLのImageタイプは、デフォルトではアニメーションGIFをサポートしていません。最初のフレームのみが表示されます。

トラブルシューティング

  1. AnimatedSprite または AnimatedImage (Qt Quick Controls 2)
    • アニメーションGIFを表示するには、Qt Quick Controls 2 の AnimatedImage タイプを使用するのが最も簡単です。
    import QtQuick.Controls 2.0 // 必要に応じてバージョン指定
    
    AnimatedImage {
        source: "animated.gif"
        width: 100
        height: 100
    }
    
  2. Movie タイプ (Qt Quick Extras)
    • 古いバージョンや特定のユースケースでは、QtQuick.Extras モジュールに Movie タイプが存在することもありますが、AnimatedImageが推奨されます。

リソースシステム (QRC) で画像がロードできない

よくある原因

  • キャッシュの問題
    • 開発中にQRCファイルに変更を加えたが、キャッシュがクリアされていない。
  • 不正なパス
    • QMLファイル内のパスが、.qrc ファイル内のプレフィックスとファイルパスに一致していない。
  • QRCファイルのコンパイル忘れ
    • .qrc ファイルがプロジェクトに正しく追加され、ビルドシステムによってコンパイルされていない。

トラブルシューティング

  1. .pro ファイルの確認
    • Qt Creator のプロジェクトファイル (.pro) に .qrc ファイルが RESOURCES += myresources.qrc のように追加されていることを確認します。
  2. パスの再確認
    • QMLでのパスは qrc:/<prefix>/<path_to_file> の形式である必要があります。
    • 例: .qrc ファイルに <file alias="icons/myicon.png">images/icon.png</file> とあり、<qresource prefix="/"/> ならば、QMLでは source: "qrc:/icons/myicon.png" となります。
  3. プロジェクトのクリーンと再ビルド
    • Qt Creator で「ビルド」->「プロジェクトをクリーン」を選択し、その後「ビルド」->「すべてビルド」を実行します。これにより、QRCファイルが再コンパイルされます。
  4. rcc コマンドラインツールの確認
    • rcc -binary myresources.qrc -o myresources.rcc のように手動でコンパイルし、問題がないか確認することもできます。

画像がメモリを大量に消費する

よくある原因

  • 画像の不適切な解放
    • 動的に生成された Image アイテムが適切に破棄されていない。
  • 過度に大きな画像
    • 実際に表示されるサイズよりもはるかに大きな解像度の画像をロードしている。

トラブルシューティング

  1. sourceSize の活用
    • 前述の通り、sourceSize プロパティを使用して、画像をロードする際に表示サイズに近い解像度にスケーリングします。これにより、メモリフットプリントを大幅に削減できます。
    Image {
        source: "very_large_photo.jpg"
        sourceSize.width: 800 // 幅を800pxに制限してロード
        sourceSize.height: 600 // またはwidthのみでアスペクト比を維持
    }
    
  2. 適切な画像形式
    • 透過が必要なければJPEG、透過が必要ならPNGなど、用途に応じた効率的な画像形式を選択します。
  3. 不要な画像のアンロード
    • source プロパティを空文字列 ("") に設定するか、visible: false と設定することで、画像を一時的にアンロードできます。sourcenull に設定すると、リソースが完全に解放される可能性があります。
    • Loader を使ってQMLコンポーネントを動的にロード/アンロードすることで、使用しないときに画像のメモリを解放できます。

CORS (Cross-Origin Resource Sharing) エラー (WebAssembly/WebEngine)

よくある原因

  • QMLアプリケーションがWebEngineを介してWebコンテンツを表示しており、その中のImageが異なるオリジンからの画像をロードしようとしている場合に発生します。これは、ブラウザのセキュリティ制限によるものです。
  1. サーバー側の設定
    • 画像を提供するサーバー側で、CORSヘッダー (Access-Control-Allow-Origin) を適切に設定して、QMLアプリケーションのオリジンからのアクセスを許可します。
  2. プロキシの使用
    • QMLアプリケーションのバックエンドでプロキシを設定し、プロキシ経由で画像を取得するようにします。
  3. 画像の組み込み
    • 可能であれば、画像をリソースファイルとしてアプリケーションに組み込むか、同じオリジンから提供するようにします。


Image タイプはQMLで画像を表示するための基本的な要素です。様々なプロパティを使って、画像の表示方法を制御できます。

基本的な画像表示

最も基本的なImageの使用法です。

// BasicImageExample.qml
import QtQuick 2.15
import QtQuick.Window 2.15

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

    // 現在のQMLファイルと同じディレクトリにある "my_image.png" を表示
    // (例: ProjectRoot/qml/my_image.png と ProjectRoot/qml/BasicImageExample.qml)
    Image {
        id: basicImage
        source: "my_image.png" // 画像ファイルのパスを指定

        // 画像の位置とサイズ
        x: 50
        y: 50
        width: 200
        height: 150

        // fillModeのデフォルトは Image.PreserveAspectFit
        // 画像のアスペクト比を保ちつつ、指定されたwidth/heightに収まるように拡大縮小
    }

    Text {
        text: "基本的な画像表示"
        font.pixelSize: 20
        anchors.left: basicImage.right
        anchors.leftMargin: 20
        anchors.verticalCenter: basicImage.verticalCenter
    }
}

解説

  • x, y, width, height: 画像の表示位置とサイズを設定します。
  • source: 表示する画像ファイルのパスを指定します。相対パスまたは絶対パスを使用できます。

リソースシステム (QRC) からの画像表示

Qtアプリケーションでは、画像をアプリケーションバイナリに埋め込むためにリソースシステム (.qrc ファイル) を使用するのが一般的です。これにより、画像ファイルが独立して配布される必要がなくなり、管理が容易になります。

手順

  1. プロジェクトに新しいQtリソースファイルを追加します (例: images.qrc)。
  2. images.qrc を開き、画像を追加します (例: prefix="/", file="icons/logo.png")。
    • これで、QMLから qrc:/icons/logo.png としてアクセスできるようになります。
  3. .pro ファイルに RESOURCES += images.qrc が追加されていることを確認します。
// QrcImageExample.qml
import QtQuick 2.15
import QtQuick.Window 2.15

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

    // QRCから画像を表示 (例: images.qrc内に /icons/qt_logo.png があると仮定)
    Image {
        id: qrcImage
        source: "qrc:/icons/qt_logo.png" // QRCパスを指定

        anchors.centerIn: parent
        width: 300
        height: 300
        fillMode: Image.PreserveAspectFit
    }

    Text {
        text: "QRCからの画像表示"
        font.pixelSize: 20
        anchors.bottom: qrcImage.top
        anchors.horizontalCenter: qrcImage.horizontalCenter
        anchors.bottomMargin: 10
    }
}

解説

  • source: "qrc:/icons/qt_logo.png": リソースシステムから画像をロードする際の典型的なパス形式です。qrc:/ の後に .qrc ファイルで定義したプレフィックスとファイルパスが続きます。

fillMode プロパティの使用

fillMode プロパティは、widthheight で指定された領域内で画像がどのように表示されるかを制御します。

// FillModeExample.qml
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15

Window {
    width: 800
    height: 600
    visible: true
    title: "Image Fill Mode Example"

    Column {
        anchors.centerIn: parent
        spacing: 20

        Repeater {
            model: [
                "Image.Stretch",
                "Image.PreserveAspectFit",
                "Image.PreserveAspectCrop",
                "Image.Tile",
                "Image.TileVertically",
                "Image.TileHorizontally"
            ]

            Row {
                spacing: 10
                width: 700

                Image {
                    source: "my_image.png" // 適当な画像を使用
                    width: 150
                    height: 100
                    fillMode: eval(modelData) // モデルデータに基づいてfillModeを設定
                    border.color: "lightgray"
                    border.width: 1
                }
                Text {
                    text: modelData
                    font.pixelSize: 18
                    anchors.verticalCenter: parent.verticalCenter
                }
            }
        }
    }
}

解説

  • Image.Tile, Image.TileVertically, Image.TileHorizontally: 画像が指定された領域を埋めるまでタイル状に繰り返されます。
  • Image.PreserveAspectCrop: アスペクト比を維持しつつ、指定された領域を完全に埋めるように拡大縮小されます。画像の一部が切り取られる可能性があります。
  • Image.PreserveAspectFit (デフォルト): アスペクト比を維持しつつ、指定された領域に完全に収まるように拡大縮小されます。画像全体が見えますが、余白ができる場合があります。
  • Image.Stretch: アスペクト比を無視して、指定されたwidthheightに画像が引き伸ばされます。画像が歪む可能性があります。

画像のロード状態の監視 (status プロパティ)

特にネットワーク経由で画像をロードする場合など、画像のロード状態を確認することは重要です。status プロパティと onStatusChanged シグナルを使用します。

// ImageStatusExample.qml
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15

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

    Column {
        anchors.centerIn: parent
        spacing: 20

        Image {
            id: remoteImage
            // 存在しないURLや遅延するURLをシミュレート
            source: "https://example.com/non_existent_image.png" // 存在しないURL
            // source: "https://via.placeholder.com/150/0000FF/FFFFFF/?text=Loading..." // プレースホルダーでロード中を表示
            width: 200
            height: 200
            fillMode: Image.PreserveAspectFit

            // status プロパティが変更されたときに呼ばれる
            onStatusChanged: {
                switch (remoteImage.status) {
                    case Image.Null:
                        statusText.text = "ステータス: Null (ソースなし)";
                        break;
                    case Image.Ready:
                        statusText.text = "ステータス: Ready (ロード完了)";
                        console.log("画像ロード成功!");
                        break;
                    case Image.Loading:
                        statusText.text = "ステータス: Loading... (ロード中)";
                        console.log("画像ロード中...");
                        break;
                    case Image.Error:
                        statusText.text = "ステータス: Error (エラー発生)";
                        console.log("画像ロードエラー:", remoteImage.errorString);
                        break;
                }
            }
        }

        Text {
            id: statusText
            font.pixelSize: 20
            text: "ステータス: 初期化中..."
        }
    }
}

解説

  • errorString: statusImage.Error の場合に、エラーに関する詳細な文字列を提供します。デバッグに非常に役立ちます。
  • onStatusChanged: status プロパティが変化したときにトリガーされるシグナルハンドラ。
  • status: 画像のロード状態を示す列挙型プロパティ (Image.Null, Image.Ready, Image.Loading, Image.Error)。

ロード時のスケーリング (sourceSize)

非常に大きな画像をロードする場合、表示サイズに合わせてロード時にスケーリングすることで、メモリ使用量を削減し、パフォーマンスを向上させることができます。

// SourceSizeExample.qml
import QtQuick 2.15
import QtQuick.Window 2.15

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

    // 仮に "very_large_image.jpg" が 4000x3000 ピクセルだとする
    Image {
        id: scaledImage
        source: "very_large_image.jpg" // 実際の大きな画像ファイルに置き換えてください

        // ロード時に画像のサイズをこのサイズにスケーリングする
        // これにより、QMLが内部的に保持する画像データのメモリサイズが小さくなる
        sourceSize.width: 400
        sourceSize.height: 300 // 両方を設定するとアスペクト比が無視される可能性あり
        // sourceSize.width: 400 のみ設定し、heightは自動計算させるのが一般的

        x: 50
        y: 50
        width: 400 // 表示サイズ
        height: 300 // 表示サイズ
        fillMode: Image.PreserveAspectFit
    }

    Text {
        text: "ロード時にスケーリング (sourceSize)"
        font.pixelSize: 20
        anchors.bottom: scaledImage.top
        anchors.horizontalCenter: scaledImage.horizontalCenter
        anchors.bottomMargin: 10
    }
}

解説

  • アスペクト比を維持したい場合は、sourceSize.width または sourceSize.height のどちらか一方だけを設定し、もう一方は設定しないでおくべきです。
  • sourceSize.width, sourceSize.height: これらのプロパティを設定すると、QMLは画像をロードする際に指定されたサイズにスケーリングします。これにより、レンダリングされるImageアイテムのwidth/heightがいくら大きくても、元の大きな画像データ全てをメモリにロードする必要がなくなります。

アニメーションGIFの表示 (AnimatedImage)

Image タイプはアニメーションGIFをサポートしていません。アニメーションGIFを表示するには、Qt Quick Controls 2 の AnimatedImage タイプを使用します。

// AnimatedGifExample.qml
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15 // AnimatedImage を提供

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

    Column {
        anchors.centerIn: parent
        spacing: 20

        // アニメーションGIFファイルに置き換えてください
        AnimatedImage {
            source: "my_animated.gif"
            width: 200
            height: 200
            fillMode: Image.PreserveAspectFit
            // playbackRate: 0.5 // 再生速度を半分にする
            // running: false // 初期状態で再生を停止
        }

        Text {
            text: "アニメーションGIF (AnimatedImage)"
            font.pixelSize: 20
        }
    }
}
  • running: アニメーションの再生状態を制御します (true で再生、false で停止)。
  • playbackRate: 再生速度を調整できます(例: 0.5 で半分の速度、2.0 で2倍速)。
  • AnimatedImage: QtQuick.Controls モジュールで提供されるタイプで、アニメーションGIFファイルを再生できます。


QMLのImageタイプは画像を扱う上で最も一般的で便利な方法ですが、特定の要件や高度なカスタマイズが必要な場合には、いくつかの代替手段や補完的な方法があります。

ImageProvider (C++ からカスタム画像を提供する)

QMLのImageタイプは、デフォルトでファイルシステムやQRCリソースから画像をロードします。しかし、C++側で動的に生成した画像(例えば、ランタイムで描画した画像、データベースから取得した画像など)をQMLに渡したい場合、QQuickImageProviderが非常に強力なソリューションです。

特徴

  • メモリ効率
    必要に応じて画像を生成・破棄できるため、メモリ管理をより細かく制御できます。
  • カスタムロードロジック
    データベースからの読み込み、ネットワークストリームからのデコードなど、複雑な画像ロードロジックをC++で実装できます。
  • 動的画像生成
    C++コードで画像を生成し、それをQMLに提供できます。

使用例

C++側 (myimageprovider.h, myimageprovider.cpp)

// myimageprovider.h
#ifndef MYIMAGEPROVIDER_H
#define MYIMAGEPROVIDER_H

#include <QQuickImageProvider>
#include <QImage>

class MyImageProvider : public QQuickImageProvider
{
public:
    MyImageProvider() : QQuickImageProvider(QQuickImageProvider::Image) {}

    // リクエストされたIDに基づいて画像を生成
    QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize) override {
        Q_UNUSED(requestedSize); // この例ではrequestedSizeは無視

        // IDに基づいて異なる画像を生成
        if (id == "dynamic_gradient") {
            int width = 200;
            int height = 150;
            if (size) *size = QSize(width, height);
            QImage image(width, height, QImage::Format_ARGB32);
            for (int y = 0; y < height; ++y) {
                for (int x = 0; x < width; ++x) {
                    image.setPixel(x, y, qRgb(x * 255 / width, y * 255 / height, 100));
                }
            }
            return image;
        } else if (id == "circle_logo") {
            int width = 100;
            int height = 100;
            if (size) *size = QSize(width, height);
            QImage image(width, height, QImage::Format_ARGB32);
            image.fill(Qt::transparent); // 透明で埋める
            QPainter painter(&image);
            painter.setRenderHint(QPainter::Antialiasing);
            painter.setBrush(QBrush(Qt::blue));
            painter.drawEllipse(0, 0, width, height);
            painter.setFont(QFont("Arial", 20));
            painter.setPen(Qt::white);
            painter.drawText(image.rect(), Qt::AlignCenter, "Qt");
            return image;
        }
        // デフォルトの画像を返すか、無効な画像を返す
        return QImage();
    }
};

#endif // MYIMAGEPROVIDER_H
// main.cpp (QMLエンジンにプロバイダを登録)
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "myimageprovider.h" // 作成したImageProviderをインクルード

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

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

    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    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://myimageprovider/dynamic_gradient" の形式でアクセス
        Image {
            source: "image://myimageprovider/dynamic_gradient"
            width: 200
            height: 150
            fillMode: Image.PreserveAspectFit
            Text { text: "C++動的グラデーション"; anchors.bottom: parent.top; anchors.horizontalCenter: parent.horizontalCenter; font.pixelSize: 16 }
        }

        Image {
            source: "image://myimageprovider/circle_logo"
            width: 100
            height: 100
            fillMode: Image.PreserveAspectFit
            Text { text: "C++動的ロゴ"; anchors.bottom: parent.top; anchors.horizontalCenter: parent.horizontalCenter; font.pixelSize: 16 }
        }
    }
}

Canvas (QMLでのカスタム描画)

Canvas QMLタイプは、JavaScript APIを使用してQML内で直接グラフィックを描画できる、HTML5 <canvas>要素に似た機能を提供します。画像ファイルを使用する代わりに、幾何学的図形、テキスト、グラデーションなどをプログラム的に描画できます。

特徴

  • パフォーマンス
    Qtのレンダリングパイプラインに統合されており、ハードウェアアクセラレーションを利用できます。
  • 動的な描画
    ユーザーインタラクションやデータ変更に基づいてリアルタイムでグラフィックを更新できます。
  • 完全なカスタマイズ性
    ピクセルレベルでの描画制御が可能です。

使用例

// CanvasDrawingExample.qml
import QtQuick 2.15
import QtQuick.Window 2.15

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

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

            // 青い四角形を描画
            ctx.fillStyle = "blue";
            ctx.fillRect(50, 50, 100, 80);

            // 赤い円を描画
            ctx.fillStyle = "red";
            ctx.beginPath();
            ctx.arc(200, 100, 40, 0, Math.PI * 2, true);
            ctx.closePath();
            ctx.fill();

            // テキストを描画
            ctx.fillStyle = "black";
            ctx.font = "24px Arial";
            ctx.fillText("QML Canvas!", 50, 200);

            // グラデーションを描画
            var gradient = ctx.createLinearGradient(0, 0, myCanvas.width, 0);
            gradient.addColorStop(0, "green");
            gradient.addColorStop(1, "yellow");
            ctx.fillStyle = gradient;
            ctx.fillRect(0, 250, myCanvas.width, 50);
        }

        MouseArea {
            anchors.fill: parent
            onClicked: myCanvas.requestPaint() // クリックで再描画をリクエスト
        }
    }
}

解説

  • requestPaint(): Canvasの内容を再描画する必要がある場合に呼び出します。これにより、onPaintハンドラが再度実行されます。
  • onPaint: 描画が行われる際に呼び出されるハンドラです。この中でgetContext("2d")を使って描画コンテキストを取得し、JavaScriptの描画APIを使用します。

ShaderEffect (GPUによる画像操作・生成)

より高度なグラフィック処理やリアルタイムのエフェクトを画像に適用したい場合、ShaderEffectが非常に強力です。これはOpenGL (またはVulkan/Direct3D) のシェーダー言語 (GLSL) を使用して、GPU上でピクセルごとに計算を実行します。

特徴

  • 画像生成
    完全な画像をシェーダー内で生成することも可能です。
  • リアルタイムエフェクト
    ブラー、カラー調整、ディストーションなど、様々な視覚効果を動的に適用できます。
  • GPUアクセラレーション
    複雑な画像処理を高速に実行できます。

使用例

// ShaderEffectExample.qml
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import QtQuick.Scene3D 2.15 // ShaderEffectItem のため

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

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

        // ShaderEffectItem を使用してカスタムシェーダーを適用
        ShaderEffect {
            width: 200
            height: 200
            anchors.centerIn: parent
            property var sourceImage: "my_image.png" // Image をソースとして渡す
            property real hueAdjust: 0.0 // 外部から調整可能なプロパティ

            // フラグメントシェーダー (GLSL)
            // このシェーダーは画像をグレースケールに変換し、色相を調整する
            fragmentShader: "
                #version 130
                uniform sampler2D sourceImage; // QMLのsourceImageプロパティに対応
                uniform lowp float hueAdjust;   // QMLのhueAdjustプロパティに対応
                varying highp vec2 qt_TexCoord0; // テクスチャ座標

                // RGBをHSVに、HSVをRGBに変換する関数 (簡易版)
                vec3 rgb2hsv(vec3 c) {
                    vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
                    vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
                    vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
                    float d = q.x - min(q.w, q.y);
                    float e = 1.0e-10;
                    return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
                }

                vec3 hsv2rgb(vec3 c) {
                    vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
                    vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
                    return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
                }

                void main() {
                    vec4 color = texture2D(sourceImage, qt_TexCoord0);

                    // グレースケール変換 (輝度法)
                    // float gray = dot(color.rgb, vec3(0.299, 0.587, 0.114));
                    // color.rgb = vec3(gray);

                    // 色相調整 (HSV変換)
                    vec3 hsv = rgb2hsv(color.rgb);
                    hsv.x = mod(hsv.x + hueAdjust, 1.0); // 色相を調整
                    color.rgb = hsv2rgb(hsv);

                    gl_FragColor = color;
                }
            "
        }

        Slider {
            anchors.horizontalCenter: parent.horizontalCenter
            anchors.top: parent.top
            anchors.topMargin: 20
            width: parent.width * 0.8
            from: -0.5
            to: 0.5
            value: 0.0
            onValueChanged: parent.findChild(function(o){ return o.hasOwnProperty('hueAdjust'); }).hueAdjust = value;
            ToolTip.text: qsTr("色相調整: %1").arg(value.toFixed(2))
            ToolTip.visible: pressed || hovered
        }
    }
}

解説

  • qt_TexCoord0: シェーダー内でテクスチャ座標にアクセスするために使用される組み込みのvarying変数です。
  • uniform: QMLのプロパティをシェーダー内でアクセスするためのキーワードです。
  • fragmentShader: GLSLで書かれたフラグメントシェーダーのコードを文字列で指定します。このシェーダーは各ピクセルの最終的な色を計算します。
  • sourceImage: QMLのImageアイテムのsourceプロパティのように、画像ファイルを指定できます。この画像はシェーダーのテクスチャとして入力されます。
  • ShaderEffect: QMLでカスタムシェーダーを適用するためのアイテムです。

MediaPlayer (動画からのフレーム抽出)

厳密には画像を直接扱うものではありませんが、QtMultimediaモジュールのMediaPlayerVideoOutputを組み合わせることで、動画のフレームをQMLに表示できます。これにより、動画を背景にしたり、動画の一部を画像のように使用したりするようなユースケースで利用できます。

特徴

  • フレームアクセス
    特定のフレームを「画像」として表示するような応用も考えられます。
  • 動画再生
    動画ファイルを直接再生できます。

使用例

// VideoAsImageExample.qml
import QtQuick 2.15
import QtQuick.Window 2.15
import QtMultimedia 5.15 // MediaPlayer, VideoOutput を提供

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

    MediaPlayer {
        id: player
        source: "qrc:/my_video.mp4" // QRCに動画ファイルを追加するか、ファイルパスを指定
        autoPlay: true
        loops: MediaPlayer.Infinite // ループ再生
    }

    VideoOutput {
        id: videoOutput
        source: player
        anchors.fill: parent
        fillMode: VideoOutput.PreserveAspectFit // 動画のアスペクト比を維持
    }

    Text {
        text: "動画を画像のように表示"
        font.pixelSize: 24
        color: "white" // 動画の上で見やすいように色を変更
        anchors.centerIn: parent
        z: 1 // VideoOutputの上に表示されるようにする
    }
}
  • VideoOutput: MediaPlayerからの映像ストリームを表示します。
  • MediaPlayer: 動画ファイルの読み込みと再生を制御します。