月刊ソフト作り!
Make The Software For VisualBasic


『ビットマップビューアを作る(その1)』
2002.Apr.17

 Presented by kouta_y
感想等は掲示板、苦情はメールへ。

会社
Windows標準画像のBMP(ビットマップ)用ビューアを作ります


ビットマップとは

数多くある画像形式の1つです。
ウィンドウズでは一番標準的な画像形式ですが、インターネットでは一番使われない画像形式でもあります。
ビットマップファイルとは、簡単に言えばRGBパタンをそのまま格納しているものです。
それ以外の画像形式は大抵「圧縮」をしており、ファイルサイズをできるだけ小さくするような工夫が凝らされています。
しかし画像の圧縮というのは、ほとんど「非可逆圧縮」と呼ばれる圧縮方法で、例えばJPEG形式で圧縮率を高くして保存してしまうと画像が乱れてしまいます。
その代わりファイルサイズは極小となります。
逆にビットマップの場合、RGBパタンをそのまま保存しているので、より鮮明な画像表示が可能となります。
その代償として、ファイルサイズは大きくなります。

インターネットでは、サーバーの容量制限やダウンロードする人の為などの理由で「ファイルサイズはなるべく小さく」という心がけがされています。(例外を除き)
なのでいくら画像が綺麗だと言っても、ファイルサイズの大きいビットマップ画像はオンラインでは嫌われています。


DIBとDDB

さて、これを知らなければビットマップは語れません。
ビットマップには大きく分けて2つの形式があり、それがDIBとDDBです。
しかし今回の「その1」では、これについては深く説明しません。

「語れない」と言ってるくせに「説明しない」とはどういう事だ!
と憤慨するかもしれませんが、正直DIBとDDBを理解するのはとても大変です。
筆者も両者の違いが分かるまで数ヶ月を要しました。

1度の説明でいきなり理解は出来ません。
何度か説明を読んでみて、少しずつ理解をしていってください。
いっぺんに理解しようとすると、必ず挫折します。
すこーしずつ分かっていけば、それで十分です。
ビットマップの形式さえ理解できれば、他の画像形式も怖くはありません。

簡単に説明をします。
これ以降もDIB、DDBという言葉を使いますが、これは2度目以降に読む場合の為です。
最初はなんとなく分かってもらえれば結構です。

DIBとは

Device Independent Bitmap
の略で、「デバイスに依存しないビットマップ」という意味で「デバイス独立ビットマップ」とも呼ばれます。
デバイスとは直訳で「装置」という意味ですが、この場合「ディスプレイ」や「プリンタ」など実際に表示させる相手の事をさします。
その「装置」もしくは「対象物」に依存をしないという事です。

DIBの構造はBITMAPINFO構造体で表します。

'DIB情報ヘッダ
Public Type BITMAPINFOHEADER
  biSize      As Long  ' 構造体のサイズ
  biWidth     As Long  ' 画像の横幅
  biHeight     As Long  ' 画像の高さ
  biPlanes     As Integer ' プレーン数
  biBitCount    As Integer ' ビット数
  biCompression  As Long  ' 圧縮コード(RLE圧縮)
  biSizeImage   As Long  ' 画像のサイズ(バイト)
  biXPelsPerMeter As Long  ' 水平解像度
  biYPelsPerMeter As Long  ' 垂直解像度
  biClrUsed    As Long  ' カラーテーブルの数
  biClrImportant  As Long  ' 重要な色の数(0〜biClrImportantまで)
End Type

'1ピクセルの色
Public Type RGBQUAD
  rgbBlue   As Byte ' 青
  rgbGreen   As Byte ' 緑
  rgbRed    As Byte ' 赤
  rgbReserved As Byte ' リザーブ
End Type

'DIB情報
Public Type BITMAPINFO
  bmiHeader  As BITMAPINFOHEADER
  bmiColors() As RGBQUAD
End Type


今のウィンドウズパソコンの拡張子.bmpのファイルはこの構造、つまりDIB形式でファイルへ保存されます。


DDBとは

Device Dependent Bitmap
の略で、「デバイスに依存するビットマップ」という意味で「デバイス依存ビットマップ」とも呼ばれます。
ディスプレイなどのデバイスに依存をします
互換があるとも言います。
「依存をする」というのは、ディスプレイなどのデバイスの色数や解像度によってビットマップを表示させるという事です。

DDBの構造はBITMAP構造体で表します。

'DDBの情報
Public Type BITMAP
  bmType    As Long  ' タイプは0
  bmWidth    As Long  ' 画像の横幅
  bmHeight   As Long  ' 画像の高さ
  bmWidthBytes As Long  ' 画像の1行のバイト数
  bmPlanes   As Integer ' プレーン数
  bmBitsPixel  As Integer ' ビット数
  bmBits    As Long  ' ビットマップパタンへのポインタ
End Type


DDBはプログラム上からビットマップを表示させる場合に使います。
BitBlt関数などはDDBを操作します。


作る

では、よく分からない説明は終わりにして早速ビットマップを表示させるものを作ってみましょう。
何を作るかと言うと、ファイルからビットマップを直接読み込みウィンドウに表示させるというものです。
つまりDIBを直接ウィンドウに描画します

まずプロジェクトは「標準EXE」を選び、フォームのプロパティを以下の様に設定します。
プロジェクトの名前は「MyView」という名前にします。

デフォルトから変更のあったものです。
Form1
AutoRedraw True
BorderStyle 1 - 固定(実線)
MaxButton False
ScaleMode 3 - ピクセル

次に「標準モジュール」を追加し、「Sub Main」プロージャを作りプロジェクトのスタートアッププロージャをSub Mainに変更します。

階層



宣言

今回はAPIは1個だけです。
使う関数は「SetDIBitsToDevice」です。
Public Declare Function SetDIBitsToDevice Lib "gdi32" ( _
  ByVal hDC As Long, _
  ByVal X As Long, _
  ByVal Y As Long, _
  ByVal dx As Long, _
  ByVal dy As Long, _
  ByVal SrcX As Long, _
  ByVal SrcY As Long, _
  ByVal Scan As Long, _
  ByVal NumScans As Long, _
  Bits As Any, _
  BitsInfo As Any, _
  ByVal wUsage As Long _
) As Long

この関数はDIBを直接デバイスに描画します。
hDC、転送先のデバイスコンテキストハンドルを指定します。
X,Y、転送先の左上隅XとYの座標。
dx,dy、転送先の横幅と高さ。
SrcX,SrcY、転送元の左上隅XとYの座標
Scan、最初の走査線を指定します。走査線とは行の事です。
NumScans、走査線の数を指定します。1度に全て描画する場合は高さを指定します。
Bits、ビットマップパタンへのポインタを指定します。
BitsInfo、DIBの情報が入ったBITMAPINFO構造体を指定します。
wUsage、カラー情報のフラグを指定します。RGBパタンならDIB_RGB_COLORSを、パレットインデックスならDIB_PAL_COLORSを指定します。


コードは

今回はイベントは一切ハンドラしません。
全てスタートアッププロージャに記述します。
表示させるビットマップファイルは引数として貰います。

ではSub Mainのコードです。

Public Sub Main()
Dim BFH   As BITMAPFILEHEADER
Dim BIH   As BITMAPINFOHEADER
Dim Bit()  As Byte
Dim Cmd   As String
Dim Argv() As String
Dim Argc  As Long
Dim NO   As Integer


Cmd = Command$()
Argc = ArgvArgc(Cmd, Argv)
If Argc <= 0 Then Exit Sub

NO = FreeFile()
Open Argv(0) For Binary As #NO
  Get #NO, , BFH
  If BFH.bfType <> BM Then
'    ビットマップではない
    MsgBox "形式エラー"
    Close
    Exit Sub
  End If
  Get
#NO, , BIH
  If BIH.biBitCount < 16 Then
'    16Bit未満
    MsgBox "色数エラー"
    Close
    Exit Sub
  End If

'  ReDim Bit(BIH.biSizeImage - 1) As Byte
  ReDim Bit(BIH.biHeight * BIH.biWidth * 4) As Byte
  Get #NO, , Bit
Close

Load Form1
Form1.Width = (BIH.biWidth * Screen.TwipsPerPixelX) + _
       (Form1.Width - (Form1.ScaleWidth * Screen.TwipsPerPixelX)) ' Twipに変換
Form1.Height = (BIH.biHeight * Screen.TwipsPerPixelY) + _
        (Form1.Height - (Form1.ScaleHeight * Screen.TwipsPerPixelY)) ' Twipに変換
Form1.Caption = Argv(0)

'DIBを転送
Call SetDIBitsToDevice(Form1.hDC, 0, 0, BIH.biWidth, BIH.biHeight, _
                       0, 0, 0, BIH.biHeight, Bit(0), BIH, 0)

Form1.Show
End Sub


まず受け取った引数の解析をします。
サンプルでは第一引数のみ使用します。

次に受け取ったパスを開き、BITMAPFILEHEADER構造体を取得します。
その後、BITMAPINFOHEADER構造体を取得、続けてビットパタンを取得しています。
サンプルでは16ビット未満のビットマップは表示させません。8ビット(256色)以下のビットマップの場合コードが難しくなる為、簡易化させる為です。

ファイルを読み込んだら、メインフォームをロードします。
まだ表示はされません。

次にフォームの横幅と高さを設定します。
ビットマップ画像の大きさに合わせます。
Width、Heightプロパティの単位はTwipの為、ピクセルをTwip単位に変換すると同時に、フォームのクライアント領域も計算しています。

SetDIBitsToDevice関数で、読み込んだDIBを渡してフォームに描画します。
最後にフォームを表示させます。


引数の解析

最初に呼び出しているArgvArgc関数は自作関数です。
コードはサンプルプロジェクトを見てください。



こんな感じ。


終わり

今回はDDBには一切触れていません。
DIBを直接描画しているので、デバイスによって色が変わるなどといった現象もおきません。
次回は256色なども表示できるものを作ります。

今回のサンプルプロジェクト
vb06.lzh(6.94KB)