「Krita」で「Python」をプログラミングしてはじめての画像を描こう
はじめに
第1回で予告した通り、今回から早速実際に「Python」をプログラミングして「Krita」にグラデーション矩形とランダムな正方形を描画していきます。
その前に、まずウィンドウにイメージを描画するテンプレート(ひな形)を作っていきます。そのためにはドキュメントを開いたり、開いたドキュメントを選択したりしてそのドキュメントでレイヤーを作り、そのレイヤーにイメージを描画します。
なお、KritaでPythonをスクリプティングするための「スクリプター」の使い方は第1回を参考にしてください。今後の連載では、スクリプターの使い方に関する説明は割愛させていただきます。
ドキュメントとイメージとレイヤー
ドキュメントとは、絵を描くウィンドウのことです。KritaではMDI(マルチドキュメントインターフェース)で複数のドキュメントを並行して処理できます。
イメージとは、ドット絵を描いたことがある方なら分かると思いますが、色の付いたドット(ピクセル、点)をたくさん描いた画像のことです。コンピュータのイメージはドットの集まりです。
一方のレイヤーとは、アニメでいうセル画のような透明な層のことです。例えば顔の絵を描くときレイヤーごとに皮膚、目、鼻、口、耳、眉毛、髪の毛などと層を重ねて描くと、それぞれのパーツの位置を変えたり形を変えたりなどが簡単にできるので、コンピュータで絵を描く場合には必須の機能です。
ドキュメントの作成
次のサンプルスクリプト「doc.py」をスクリプティングして「▷(実行)」ボタンを押すと、図1のような真っ白なイメージのレイヤーのあるドキュメントウィンドウが現れます。どのような幅高さのドキュメントを作っても、最初にウィンドウが表示されるときは描画領域がウィンドウのサイズにピッタリ拡大縮小されて表示されます。
・サンプルスクリプト「doc.py」# ドキュメントの作成 doc = Krita.instance().createDocument(256,256, "Document name", "RGBA", "U8", "", 300.0) Krita.instance().activeWindow().addView(doc)
【サンプルスクリプトの解説】
「createDocument」メソッドで256x256pxのサイズで"Document name"という名前の赤(Red)、緑(Green)、青(Blue)、アルファ(Alpha)がU8(符号なし8ビット)、プロファイルなし、解像度300のドキュメントを作成します。
アクティブなウィンドウのビューにドキュメントを追加(addView)します。
イメージの作成
次のサンプルスクリプト「image.py」のように追記して「▷(実行)」ボタンを押すと、また図2のような真っ白なイメージのレイヤーのあるドキュメントウィンドウが現れます。透明な256x256pxのイメージを作ったのですが、まだレイヤーに描画していないため何も起こらず真っ白なままです。
ドキュメントの作成は「create_doc」関数にまとめました。この関数では戻り値でドキュメントのインスタンスを受け渡しします。
・サンプルスクリプト「image.py」# モジュール from PyQt5.Qt import * # ドキュメントの作成 def create_doc(width,height): doc = Krita.instance().createDocument(width,height, "Document name", "RGBA", "U8", "", 300.0) Krita.instance().activeWindow().addView(doc) return doc # ドキュメントの取得 doc = create_doc(256,256) # イメージの用意 pixmap = QPixmap(doc.bounds().size()) pixmap.fill(QColor(Qt.transparent)) img = pixmap.toImage()
【サンプルスクリプトの解説】
「import」文で「PyQt5」モジュールを読み込みます。
「pixmap = QPixmap(doc.bounds().size())」は本来なら「pixmap = PyQt5.Qt.QPixmap(doc.bounds().size())」と書くところ、import文で「*」と書いているので「PyQt5.Qt」を書かなくてもこのモジュールの機能が使えます。
「QPixmap」クラスのインスタンスでイメージを生成し、透明で塗りつぶし(fill)、「QImage」形式に変換します。
レイヤーの作成
次のサンプルスクリプト「layer.py」のように追記して「▷(実行)」ボタンを押すと、図3のような透明なイメージのレイヤーのあるドキュメントウィンドウが現れます。ここで透明なイメージをアクティブなレイヤーに描画しました。
これで、ひと通りドキュメントを作成してイメージを描き、レイヤーにイメージをセットするところまでできました。イメージの用意は「draw_img」関数にまとめました。今後、たいていこういった名前の関数内で中心となる絵をプログラミングして形を描きます。
・サンプルスクリプト「layer.py」# モジュール from PyQt5.Qt import * # ドキュメントの作成 def create_doc(width,height): doc = Krita.instance().createDocument(width,height, "Document name", "RGBA", "U8", "", 300.0) Krita.instance().activeWindow().addView(doc) return doc # イメージの用意 def draw_img(doc): pixmap = QPixmap(doc.bounds().size()) pixmap.fill(QColor(Qt.transparent)) return pixmap.toImage() # ドキュメントの取得 doc = create_doc(256,256) # イメージの取得 img = draw_img(doc) # 修正箇所 root = doc.rootNode() # 修正箇所 layer = root.childNodes()[0] # イメージのバイト長の検証 if img.sizeInBytes() == 4 * layer.channels()[0].channelSize() * doc.width() * doc.height(): ptr = img.bits() ptr.setsize(img.byteCount()) layer.setPixelData(QByteArray(ptr.asstring()), 0, 0, doc.width() , doc.height()) else: print('Error') # リフレッシュ doc.refreshProjection()
【サンプルスクリプトの解説】
アクティブなレイヤーを取得します。他にもレイヤー名でアクティブとは限らないレイヤーも取得できます。
「if」文でイメージのバイト長が4×チャンネルサイズ×幅×高さと等しいか検証します。等しくなければ'Error'をターミナルに表示します。
イメージの幅高さと同じ大きさでレイヤーの中にイメージを描画します。
ドキュメントをリフレッシュ(refreshProjection)することでレイヤーへの描画を反映します。
テンプレートの完成
次のサンプルスクリプト「template.py」のように書き換えて「▷(実行)」ボタンを押すと、また透明なイメージのレイヤーのあるドキュメントウィンドウが現れます。プログラミングはテンプレートのような型になるスクリプトの部分まで深く理解していなくても大丈夫です。自分が書き足すスクリプトについてだけ理解していれば、他は後から何となく分かってくるものです。
レイヤーのセットは「set_layer」関数にまとめました。このテンプレートでKritaを使った基本的な描画処理はひと通り完成です。あとは主にこのテンプレートに肉付けしていくだけです。
・サンプルスクリプト「template.py」# モジュール from PyQt5.Qt import * # ドキュメントの作成 def create_doc(width,height): doc = Krita.instance().createDocument(width,height, "Document name", "RGBA", "U8", "", 300.0) Krita.instance().activeWindow().addView(doc) return doc # グラデーションの描画 def draw_img(doc): pixmap = QPixmap(doc.bounds().size()) pixmap.fill(QColor(Qt.transparent)) return pixmap.toImage() # レイヤーのセット def set_layer(doc,img): # 修正箇所 root = doc.rootNode() # 修正箇所 layer = root.childNodes()[0] if img.sizeInBytes() == 4 * layer.channels()[0].channelSize() * doc.width() * doc.height(): ptr = img.bits() ptr.setsize(img.byteCount()) layer.setPixelData(QByteArray(ptr.asstring()), 0, 0, doc.width() , doc.height()) else: print('Error') # メイン関数(main()という関数名にしたいところだが既に使われている) def begin_draw(): doc = create_doc(256,256) img = draw_img(doc) set_layer(doc,img) # リフレッシュ doc.refreshProjection() # メイン関数の呼び出し begin_draw()