2025/11/30

QR コードの作画:#5)画像データの変換

今回はビットマップの作成で一番重要な画像データを作成する関数 xBMP_MakePictureData を作成します。少し長い関数になりますので、ポイントとなる点を順にまとめます。最後に関数全体を掲載していますので、そちらを参照しながら読み進めてください。


関数の機能

この関数の機能は QR コードの論理データからビットマップファイルに設定する画像データブロックを作成することですが、データブロックのサイズ(バイト数)を事前に把握する役割もあります。前回 記載しましたが、ビットマップを出力する際に、画像データブロックより先に、ヘッダブロック出力が必要だったためです。

そこで関数のインターフェースは次のように定義します。

◇ 引数

1 vabQR Variant QR コードの論理データ
(Boolean 型の 2 次元配列)
2ravBitMap  Variant (戻り値)画像データ(Variant 型の配列)
1要素に1行分の画像データを保持

ravBitMap で渡される変数は、前回 紹介したメインルーチンで行数分の配列として宣言されていました。

      ReDim avBitMap(UBound(vabQR, 2))     '保存エリア確保
      Call xBMP_MakePictureData(vabQR, avBitMap)

この変数の各要素に 1 行ごとの画像データを格納します。1行分の画像データは1行を構成する Byte 型配列とします。保持仕様を図式化すると次のようになります。


1行分のバイト数

白黒ビットマップの場合、横幅のピクセル数が必要なビット数となります。これをバイトに変換するのですが、エンコード仕様により、4 バイト単位に調整する必要がありました。

具体的には、以下の演算でバイト数を算出して、保存エリアを確保しています。

   '初期化(1行分の保存に必要なエリアを確保)
   iByte = UBound(vabQR, 1) + 1    '画像の幅
   iByte = (iByte + 7) \ 8                       '必要なビット数(バイト単位)
   iByte = ((iByte + 3) \ 4) * 4             '1行は4バイト単位
   ReDim abyLine(iByte - 1)


1行分のデータ作成

ビットマップは画像の下から上にエンコードする仕様でした。これを実現しているのが以下の For ループで、Step が -1 となっています。

   For iY = UBound(vabQR, 2) To 0 Step -1     '行は下から上

ループ内ではまず、1 行分のワークエリアである abyLine を初期化しています。初期化で全要素をいったん 0 にしておき、必要な要素だけ値をセットしようという算段です。

      '1行分のワークエリアの初期化
      iByte = 0
      ReDim abyLine(UBound(abyLine))     '配列内初期化

コード内のコメントの ① の部分は、1 行分のQR コード論理データを順に処理しています。処理はサブルーチン SetBit で行い、8 ビット分たまったら、WriteByte サブルーチンでバイトに変換して、1 行分のワークエリアに格納しています。

画像の幅が 8 の倍数ではないとき、余ったビットを格納するために ② の処理を実行しています。


関数全体

最後に関数 xBMP_MakePictureData の全体を掲載します。

%REM
QR コードの論理データ(Boolean 型の 2 次元配列)から画像データブロックを作成します。

◆ 引数
   vabQR Boolean QR コードの論理データ(Boolean 型の 2 次元配列)
   ravBitMap Variant 画像データ(配列の各要素は1行分の画像データ(Byte 配列))
%END REM

Private Function xBMP_MakePictureData(vabQR As Variant, ravBitMap As Variant) As Boolean
   Dim iY As Integer
   Dim iX As Integer
   Dim iB As Integer                 'ビットカウンタ
   Dim abBit() As Boolean    '1バイト分を保存するワークエリア
   Dim bBit As Boolean
   Dim byByte As Byte
   Dim iByte As Integer         '1行のバイト数(カウンタ)
   Dim abyLine() As Byte     '1行分のワークエリア(バイト数分の配列)
   Dim iLine As Integer

   '初期化(1行分の保存に必要なエリアを確保)
   iByte = UBound(vabQR, 1) + 1    '画像の幅
   iByte = (iByte + 7) \ 8                       '必要なビット数(バイト単位)
   iByte = ((iByte + 3) \ 4) * 4             '1行は4バイト単位
   ReDim abyLine(iByte - 1)

   '1行ずつ順に処理
   iLine = 0
   ReDim abBit(7)     '1バイト分のビット格納エリアのリセット
   iB = 0
   For iY = UBound(vabQR, 2) To 0 Step -1     '行は下から上
      '1行分のワークエリアの初期化
      iByte = 0
      ReDim abyLine(UBound(abyLine))     '配列内初期化

      '① 画像の出力(1ピクセル毎)
      For iX = 0 To UBound(vabQR, 1)
         bBit = vabQR(iX, iY)
         GoSub SetBit
      Next

      '② 1バイトに満たないビットがあればバイトに変換
      If iB > 0 Then GoSub WriteByte

      '戻り値配列にセット
      ravBitMap(iLine) = abyLine
      iLine = iLine + 1
   Next

   Exit Function

SetBit:     '1ビットを格納
   abBit(iB) = bBit
   iB = iB + 1
   '1バイト分たまったら格納
   If iB = 8 Then GoSub WriteByte

   Return

WriteByte:     '1バイトを格納
   byByte = 0
   For iB = 0 To 7
      If abBit(iB) = False Then     '白黒反転
         byByte = byByte + 2 ^ (7-iB)
      End If
   Next

   '1バイト書き込み
   abyLine(iByte) = byByte
   iByte = iByte + 1

   '1バイト分のビット格納エリアのリセット
   ReDim abBit(7)
   iB = 0

   Return
End Function


次回の予定

これでビットマップ作製に必要な情報がそろいました。次回は、バイナリファイルに出力する部分を作成します。


前回 QR コードの作画


0 件のコメント:

コメントを投稿