2024/03/03

DXL Step-by-Step:#30)イメージリソースで QR コードを書いてみよう ②

前回に引き続き DXL を使って QR コードを作画に挑戦する話の後編です。今回はメインの部分である作画ルーチンを紹介します。


画像の準備

まず、作画に利用する画像を準備します。

QR コードの 1 マスは白か黒で構成されています。それぞれの画像をリッチテキストに並べれば QR コードができます。ただそれだとプログラミングとして芸がないので、2 x 2 マスのパターン 16 種の画像を準備します。

ファイル名は、左上、右上、左下、右下の順で 2進数としてつなぎ、16 進数に変換しています。例えば次のような感じです。

作成した画像はイメージリソースに登録しておきます。


作画ルーチンのサンプル

前回紹介できなかった xSetDXL_QR の全体像を先に紹介します。作画ルーチンはこの関数だけで構成されています。ポイントとなる点は後述のセクションで解説します。

Function xSetDXL_QR(vddn As NotesDOMDocumentNode, vdenPar As NotesDOMElementNode, vabQR() As Boolean)
   Dim denPic As NotesDOMElementNode
   Dim den As NotesDOMElementNode
   Dim x As Integer
   Dim y As Integer
   Dim by As Byte

   'QR コードをイメージリソースに変換
   For y = 0 To Int(UBound(vabQR, 2)/2)
      For x = 0 To Int(UBound(vabQR, 1)/2)
         '2 x 2 エリアのリソース番号を算出
         by = 0
         On Error Resume Next
         If vabQR(x*2, y*2) Then by = by + (2^0)
         If vabQR(x*2+1, y*2) Then by = by + (2^1)
         If vabQR(x*2, y*2+1) Then by = by + (2^2)
         If vabQR(x*2+1, y*2+1) Then by = by + (2^3)
         On Error GoTo 0

         '画像の作成
         Set den = vddn.CreateElementNode("picture")
         Set denPic = vdenPar.AppendChild(den)

         'イメージリソースの指定
         Set den = vddn.CreateElementNode("imageref")
         Call den.SetAttribute("name", Hex$(by) & ".png")
         Call denPic.AppendChild(den)
      Next

      '改行の追加
      Set den = vddn.CreateElementNode("break")
      Set denPic = vdenPar.AppendChild(den)
   Next
End Function


ループの決定

事前に作成した画像の通り、QR コードの配列を 2 x 2 で抽出しながら処理しています。よってループ数は QR コードのサイズの半分となります。これを実現しているのか以下の For ループです。

   For y = 0 To Int(UBound(vabQR, 2)/2)
      For x = 0 To Int(UBound(vabQR, 1)/2)

UBound の 2 つ目の引数は多次元配列の次元数を指定します。それを利用して、縦と横の要素数の半分でループを回しています。半分にしているのは 2 x 2 単位で処理するからですね。また、端数をなくすために Int 関数を使用して整数部だけを取得しています。

配列 vabQR 要素数が 6 x 4 だったとすると、ループ変数 x と y は以下のようになるということです(実際の QR コードではもっと大きなサイズとなります)。


リソース番号の算出

2 x 2 のエリアに該当するリソース番号を算出しているのは以下の部分です。

         '2 x 2 エリアのリソース番号を算出
         by = 0
         On Error Resume Next
         If vabQR(x*2, y*2) Then by = by + (2^0)
         If vabQR(x*2+1, y*2) Then by = by + (2^1)
         If vabQR(x*2, y*2+1) Then by = by + (2^2)
         If vabQR(x*2+1, y*2+1) Then by = by + (2^3)
         On Error GoTo 0

ループ変数 x と y を使用して vabQR の要素にアクセスします。2 倍して 1 を加算するかどうかで、上下左右の要素にアクセスしています(オレンジ)。その結果が True の場合、その位置に応じた値を加算しています())。


ところで、If 文に "=" がないので、不自然に感じる方がいるかもしれません。

         If vabQR(x*2, y*2) Then by = by + (2^0)

これは配列 vabQR が Boolean 型であるからです。要素の値が True の場合 True = True のような判定をしても意味がありません。要素の値そのものが判定結果として使えるので、省略しています。


また、今回の演算の範囲に限り On Error Resume Next でエラーを無視するようにしています。これは、配列 vabQR の要素数が奇数だった時の対策で、範囲外の要素にアクセスしたエラーを無視するためです。QR コードの場合ではありえないパターンかもしれませんが...


イメージリソースの配置

イメージリソースの配置自体は単純です。picture ノードを作成し、その下に imageref ノードを追加し、リソース名を name 属性で設定します。リソース名は、事前に計算したリソース番号を 16 進数に変換して決定しています。

         '画像の作成
         Set den = vddn.CreateElementNode("picture")
         Set denPic = vdenPar.AppendChild(den)

         'イメージリソースの指定
         Set den = vddn.CreateElementNode("imageref")
         Call den.SetAttribute("name", Hex$(by) & ".png")
         Call denPic.AppendChild(den)

ループ変数 x のループが終わると break ノードを追加しています。このノードは段落内での改行を表します。これで 2 回目のループ以降も行を変え左端から画像が配置されることになります。

      Set den = vddn.CreateElementNode("break")
      Set denPic = vdenPar.AppendChild(den)


生成される DXL

今回のサンプルで生成される DXLは次の通りです(リッチテキスト部分のみ抜粋し一部省略)。

段落 par の下にイメージリソースのノードが並びます。このような構造にすることで、イメージが連続して配置され、左から右に順に表示されます。また、1行分の出力が終わると break ノードが出力されています。これで次の行の画像を表示する準備をしているということですね。


実行結果

今回のサンプルと連載『ノーツで QR コード』のプログラムで出力した結果を比較してみました。課題だったセル間の隙間がなくなりずいぶん QR コードらしくなりました。また、サイズが小さくなったので、より大きな QR コードにも対応できそうですね。

ちなみに、今回はセルのサイズは 5 x 5 ピクセルの画像を使用しました。まだまだ小さくできる余地はありそうです...


前回 DXL Step-by-Step


0 件のコメント:

コメントを投稿