2015/08/29

Qtのグラフィックス――C++/Qtの深いところ#3

QPainterAPI、OpenGL(ES)、アニメーション
注意: Qt5.5時点の情報です。


目次

  • 目的
  • Qtのグラフィックス
    • 前置き
    • QPainterAPIの概要
    • QPainterAPIの主要機能
    • コンポーネントクラス
    • QPainterクラス
    • 四通りのアニメーション
  • OpenGL(ES)
    • OpenGLと3D開発
    • 新しくなったQOpenGLWidget
  • まとめ
  • 次回予告

目的

QtGUIを支えるQtのグラフィックスについて調査します。英語ドキュメントが多く、誤解を生みがちなので、筆者と同じ轍を踏まないようにできればと考えています。このシリーズでは、あまり詳しいコードを書くつもりはありません。C++の経験はあるけれど、Qtはこれからだという方を対象に、噛み砕いて説明します。  

Qtのグラフィックス

前置き

Qtにおいて、グラフィックス機能は主にQPainterAPI、およびQtQuick2以降のScene Graphを通して扱われます。Qt5以降のQPainterAPIは、画像のプリントや、様々な画像形式の読み書きにも対応しています。本稿では、QPainterAPIを中心に解説します。

QPainterAPIの概要

ベクターグラフィックス、テキストイメージ、画像ファイル、ウィジェット、プリント、QPainterAPIはこれらすべてに対して、充実した機能を提供します。アプリケーションへの実際の描画は、QPainterEngineが行います。このクラスは、OpenGL(ES)をバックエンドとしてラッピングし、クロスプラットフォームかつ、高性能、高品質な描画を実現します。 厳密には、QPainterEngineはQOpenGLPaintDeviceで描画、QBackingStoreで画像バッファの保存を行います。QtQuick2以前は、そのグラフィックスにもQPainterAPIが使われていましたが、現在はOpenGLを直接扱うSceneGraphに置き換わりました。ちなみに、筆者の貧弱なノートパソコン環境では、なぜかSceneGraphが動きません。

QPainterAPIの主要機能

  • ペイントシステム
    • QImage(ハード系に依存しない画像ピクセルを扱います)
    • QPixmap(スクリーンに描画するための画像を扱います)
    • QWidget(UIクラスを提供します)
    • QOpenGLWidget (後述)
  • XY座標系システム(座標系に加えて、アンチエイリアシングや変形を提供します)
  • 線描画と塗りつぶし(パス描画やアンチエイリアシング、グラデーションを提供します)

コンポーネントクラス

QtGUIにはQtのグラフィックス開発を効果的にするためのコンポーネントクラスが含まれています。以下はその一部をわかりやすく分類したものです。なお、これらはQtGUIに付属しているため、初期状態ではQtのメタタイプではありません。(メタタイプについては、本連載の#2メタオブジェクトシステムをご覧ください)これらもまた、Q_DECL_METATYPEを使うことでメタタイプにすることができます。
(15/08/30補足) QImageは画像ピクセル操作の為に、QPixmapはスクリーン描画用に使います。QImageをQPixmapに変換することは可能ですが、パフォーマンスの低下は覚悟してください。

画像

QBitmap/QImage/QPixmap/QIcon

QColor/QGradient

ツール

QBrush/QPen/QFont

図形

QLine/QRect/QPoint/QPolygon/QVector2D

変形

QMatrix/QTransform

QPainterクラス

QWidget(すべてのUIクラスの基底クラス)には、paintEventという仮想関数が用意されています。この関数は、イベントループごとに呼び出されるものだと勘違いされることが多々あるかもしれません。プログラミングによってはそうすることもできますが、パフォーマンスを考慮した設計をしてください。詳しくは、事項で説明します。話が逸れましたが、そのpaintEvent内ではQPainterというクラスのインスタンスを一時的につくります。QPainterのコンストラクタの引数には、QWidgetのthisポインタ(QPaintDeviceの継承クラス)を入れます。QPainterAPIのあらゆる描画は、このQPainterオブジェクトで書くことができます。

四通りのアニメーション

C++/Qtでグラフィックスのアニメーションを実現する方法は、筆者が試した限りでは四通りあります。
一つ目は、Qtのアニメーションフレームワークを使う方法です。これを使うと、Qtで描画する図形の状態遷移を細かく指定することができます。この方法の欠点は、ゲームのように常に状態が無作為に変わっていくアニメーションとしては使いにくいところです。一般的なGUIのアニメーションには適しています。
二つ目は、Qtのタイマーイベントなどを直接的に使う方法です。タイマーが回ってきた時点や、何らかの入力操作が起きた時点で、QWidgetのrepaint関数を呼び出します。すると、ウィジェットはQWidgetのpaintEvent関数のオーバーライド関数に従って直ちに再描画します。描画優先の場合に使います。
三つ目は、Qtのイベントループを使う方法です。この方法では、スクリーンに何か変更を加えた時点、もしくは任意のタイミングで、QWidgetのupdate関数を呼び出します。updateされた後のウィジェットは、イベントループが回ってきた時に、paintEvent関数のオーバーライド関数を使って再描画します。処理優先の場合に使います。
四つ目は、Graphics Viewフレームワークを使う方法です。多くの画像や図形を一度に描画する場合に使われます。描画まわりの面倒な環境構築が自動化されます。これに関しては次回の記事で詳しく解説する予定です。
注意するべきことがあります。よくpaintEvent関数内でrepaint関数やupdate関数を呼び出しているコードがあります。連続的に描画はされますが、未定義の動作なので控えましょう。

OpenGL(ES)

OpenGLと3D開発

クロスプラットフォームUIのOpenGL(ES)開発がしたい、という目的でQtを使う方も多いのではないでしょうか。ご存知かもしれませんが、QtはOpenGLを利用したクロスプラットフォームの3D描画にも対応しています。古くはQt OpenGLモジュールとして提供されていましたが、Qt5.0以降はQt GUIモジュールに統合されました。QtのOpenGLクラスは、次回記事で解説する予定のGraphics View フレームワークと連携することも可能です。

新しくなったQOpenGLWidget

Qt5.4で新しいOpenGL用のクラスがつくられました。QOpenGLWidgetです。これはQGLWidgetの代替となります。変更点に関しては、Qt公式ブログの記事 "Qt Weekly #19: QOpenGLWidget"を参考にしてください。QOpenGLWidgetのより詳しい扱い方は、このブログでも、いずれ扱いたいと考えています。

まとめ

ザックリとした記事でしたが、ここまで読んでくださいまして、ありがとうございました。テストコードが少なかったでしょうか。Qtドキュメントに多くのサンプルが載せられているので、なるべく紙面を簡略化するためにも、この記事では省略してみました。
Qtをあまり知らなかったという方は、Qtに少しでも興味を持っていただければ幸いです。また、Qtを今利用しているという方は、こういうこともできる、とか、これは知らなかった、ここはこうだ、などの感想を頂けると幸いです。ご意見、ご質問等も可能な範囲で受け付けております。

※この記事はQt公式ドキュメント(doc.qt.io)を参考に書かれました。

次回予告

次回は引き続き、Qtのグラフィックスの一つである、Graphics View フレームワークを解説します。Graphics View フレームワークでは、複雑な図表も本格派ゲームも、Qtにおいて最適化されたパフォーマンスで描画できる仕組みが用意されています。次回はその深いところを解説します。

0 件のコメント:

コメントを投稿