2025/12/01

QR コードの作画:#6)ビットマップファイルの出力

前回はビットマップの画像データを作成する関数 xBMP_MakePictureData を作成しました。今回はメインルーチンからコールしているその他の関数を紹介します。

  • ファイルヘッダの出力
  •  情報ヘッダの出力
  •  カラーパレットの出力
  • 画像データの出力

これらは『#2)白黒ビットマップの構造』ビットマップの構造順にファイルを出力するための処理となります。また、各ブロックの構造については『#3)ビットマップのエンコード仕様』を参照ください。


ファイルヘッダの出力

ファイル出力するエリアとなる Byte 型配列を用意して、そこに出力する値をセットします。すべての値が準備できたら、NotesStream の Write メソッドでファイルに書き込みます。

ファイルヘッダで画像に応じて可変になる項目はファイルサイズだけでした。

ファイルサイズは、前回作成した画像データから取得し、他のブロック数を加算して算出ります。それ以外の項目は固定の値となります。

Private Function xBMP_WriteFileHeader(vnst As NotesStream, vvBitMap As Variant)
   Dim lSize As Long
   Dim sSize As String
   Dim i As Integer
   Dim abyFileHeader(13) As Byte

   '固定
   abyFileHeader(0) = Asc("B")
   abyFileHeader(1) = Asc("M")

   'ファイルサイズ
   lSize = Ubound(vvBitMap(0)) + 1               '1行のバイト数
   lSize = lSize * (UBound(vvBitMap) + 1)    '行数を掛ける
   lSize = lSize + 14 + 40 + 8     '画像データ以外のブロックを加算
   'リトルエンディアンでセット(4 バイト)
   sSize = Right("0000000" & Hex(lSize), 8)
   For i = 0 To 3
      abyFileHeader(5-i) = CByte(xHexToDec(Mid(sSize, i*2+1, 2)))
   Next

   'オフセットビット
   abyFileHeader(10) = 62

   'ファイルヘッダ部をファイルに出力
   Call vnst.Write(abyFileHeader)
End Function


情報ヘッダの出力

情報ヘッダで可変となる項目は、画像のサイズ(幅と高さ)、画像データのバイト数でした。

画像データのサイズはファイルヘッダで算出していたのと同様です。画像サイズは、QR コードの論理データ(Boolean 型の 2 次元配列)から簡単に取得できます。よってこの関数の引数には、QR コードの論理データ vabQR と画像データ vvBitMap が引数になっています。

Private Function xBMP_WriteInfoHeader(vnst As NotesStream, vabQR As Variant, vvBitMap As Variant)
   Dim lSize As Long
   Dim sSize As String
   Dim i As Integer
   Dim abyInfoHeader(39) As Byte

   '情報ヘッダのサイズ
   abyInfoHeader(0) = 40

   '画像の幅(リトルエンディアン)
   lSize = (UBound(vabQR, 1) + 1)
   sSize = Right("0000000" & Hex(lSize), 8)
   For i = 0 To 3
      abyInfoHeader(7-i) = CByte(xHexToDec(Mid(sSize, i*2+1, 2)))
   Next

   '画像の高さ(リトルエンディアン)
   lSize = (UBound(vabQR, 2) + 1)
   sSize = Right("0000000" & Hex(lSize), 8)
   For i = 0 To 3
      abyInfoHeader(11-i) = CByte(xHexToDec(Mid(sSize, i*2+1, 2)))
   Next

   'プレーン数
   abyInfoHeader(12) = 1

   'ビットカウント
   abyInfoHeader(14) = 1

   '画像データのサイズ(リトルエンディアン)
   lSize = UBound(vvBitMap(0)) + 1               '1行のバイト数
   lSize = lSize * (UBound(vvBitMap) + 1)    '行数を掛ける
   sSize = Right("0000000" & Hex(lSize), 8)
   For i = 0 To 3
      abyInfoHeader(23-i) = CByte(xHexToDec(Mid(sSize, i*2+1, 2)))
   Next

   '情報ヘッダ部をファイルに出力
   Call vnst.Write(abyInfoHeader)
End Function


カラーパレットの出力

今回は白黒のビットマップですのでカラーパレットブロックの内容はすべて固定でした。ですので、出力関数内は単純です。


Private Function xBMP_WriteColorPallet(vnst As NotesStream)
   Dim sCol_HEX As String
   Dim i As Integer
   Dim abyColor(7) As Byte

   'パレット0
    sCol_HEX = "00000000"
   For i = 0 To 3
      abyColor(i) = CByte(xHexToDec(Mid(sCol_HEX , i*2+1, 2)))
   Next

   'パレット1
    sCol_HEX = "FFFFFF00"
   For i = 0 To 3
      abyColor(4 + i) = CByte(xHexToDec(Mid(sCol_HEX , i*2+1, 2)))
   Next

   'カラーパレットをファイルに出力
   Call vnst.Write(abyColor)
End Function


画像データの出力

画像データは Variant 型配列の各要素に 1 行分が入っていました。ファイルへの出力は For ループで 1 行ずつ順に出力するだけです。

Private Function xBMP_WritePictureData(vnst As NotesStream, vvBitMap As Variant)
   Dim iY As Integer

   '1 行ずつファイルに出力
   For iY = 0 To UBound(vvBitMap)
      Call vnst.Write(vvBitMap(iY))
   Next
End Function


リトルエンディアンとは?

ビットマップファイルでは画像サイズやファイルサイズなど数値を出力する場合、リトルエンディアンで出力します。これは、複数バイトで表現される整数を下位バイトから順に並べる 方式となります。

例えば 16 進数で 8 桁となる数値を 4 バイトで出力するには、次のようになります。


16 進数 → 10 進数変換

リトルエンディアンのようにバイナリーファイルを扱う場合 16 進数を 10 進数に変換することがあります。10 進数 → 16 進数では Hex という関数があるのですが、逆の関数はありません。そこで関数 xHexToDec を作成します。

Private Function xHexToDec(sHex As String) As Long
   '16 進数を 10 進数に変換
   xHexToDec = xVirtualNumber2Dec(sHex, "0123456789ABCDEF")
End Function

関数は 2 段階になっています。xVirtualNumber2Dec では、引数 vsNumChrList で表現された“仮想的な進数”を 10 進数へ変換する関数 になっています。16 進数からの変換だけであればもっとシンプルに記述できますが、n 進数で使える汎用的な関数としています。

Private Function xVirtualNumber2Dec(ByVal vsNum As String, ByVal vsNumChrList As String) As Long
   'psNumChrListで構成されるLen(psNumCheList)進数のpsNumを10進数に変換
   Dim iNum As Integer
   Dim iTgetNum As Integer
   Dim sTgetNum As String
   Dim sNum As String
   Dim lResult As Long
   Dim iMinus As Integer
   Dim i As Integer
   Dim iNumChrList As Integer

   sNum = vsNum
   If Left(sNum, 1)="-" Then
      sNum = Right(sNum, Len(sNum) - 1)
      iMinus = -1
   Else
      iMinus = 1
   End If
   iNum = Len(sNum)
   iNumChrList = Len(vsNumChrList)
   lResult = 0
   For i = 1 To iNum
      sTgetNum = Mid(sNum, i, 1)
      iTgetNum = InStr(1, vsNumChrList, sTgetNum) - 1
      lResult = lResult + iTgetNum * iNumChrList ^ (iNum - i)
   Next i

   xVirtualNumber2Dec = lResult * iMinus
End Function


前回 QR コードの作画 前回


0 件のコメント:

コメントを投稿