TOP English

透明度付 BMP のフォーマット(2)

Transparent BMP file format(2)

概要

ここでは、BITMAPV4 形式の透明度付 BMP (透過BitMap) ファイルの出力プログラムリスト例を示します。
なお、この BITMAPV4 は、Windows 98 のペイントブラシが出力した BITMAPV4 フォーマットに倣っています。

Following is a program list for outputting BITMAPV4 sample BMP file.
This BITMAPV4 format is based on the BMP file that was output by PaintBrush on Windows 98
when 5-5-5 bit-field 16-bpp bitmap was input.


本プログラムを実行すると、以下のような透明度付 BMP ファイル (BITMAPV4 形式) が生成されます。



本プログラムによって出力された BMP ファイルはこちら(49kB)です。
The BMP file output by following program is HERE (49kB).

なお、PNG 形式へ変換したファイルはこちら(1.2kB)です。
Converted PNG file is HERE (1.2kB).

なお、PSD 形式へ変換したファイルはこちら(61kB)です。
Converted PSD file is HERE (61kB).

また、Windows における BITMAPV4 形式 BMP ファイル対応 (互換性) について示します。


BITMAPV4 の必要性

任意の透明度を持つ透明度付 BMP (透過BitMap) ファイルを出力するには BITMAPV4 以降の形式を用いる必要があります。
Win 3.1 形式 (BITMAPINFOHEADER を使用している形式) でも、32-bit BMP というのがあり、上位 8 bit が空いているので、ここに透明度を入れられそうですが、
  https://support.microsoft.com/kb/94326 (in Japanese)
  ( => https://support.microsoft.com/search/results?query=SeeDIB )
で公開されている Microsoft 社のサンプルコード(SeeDIB) に従うと、32 bit の上位 8 bit は無視しなければならない BMP ファイルが生成されます (reserved の 上位 8 bit は、意味のないビットとして扱わなければ、正しく BMP 表示できない)。
(※2021.11.3 現在、サンプルコード SeeDIB はリンク先サーバ設定により「非公開」となっています。)

従って、結論から言うと、BITMAPINFOHEADER を使用している 32-bit BMP では、残念ながら正式には透明度を扱えず、 任意の透明度を扱うには、BITMAPV4 以降のフォーマットが必須となります。
(なお、256色以下のパレットを持つ BMP ファイルでは、パレットの0番を透明色として扱う方法が、Microsoft 社によって認められています。)

 補足情報: http://www.msfn.org/board/topic/22523-transparent-bmps/
  (2008/5/18 追記) (※2021.11.3 リンク先更新)

 補足情報: http://forums.adobe.com/message/3273974
  (2011/6/20 追記) (※2021.11.3 リンク先サーバ終了)


透明度付 BMP ファイルの出力プログラムリスト例

BITMAPV4 でセーブするには、ヘッダ中で BITMAPINFOHEADER に代えて BITMAPV4HEADER を用います。


#include <stdio.h>
#include <windows.h> 

BITMAPFILEHEADER  bfh;  // (14 bytes) 
BITMAPV4HEADER  bh;     // (108 bytes) 
DWORD  dsBitfield[3];   // (12 bytes)  // from DIBSECTION 

int savebmpv4(char *fname,long width,long height, 
              unsigned char *r,  
              unsigned char *g,  
              unsigned char *b,  
              unsigned char *a)  { 
  FILE *wfsr;
  long x,y,adr,outbytes; 
  int i; 
  
  wfsr= fopen(fname,"wb"); 
  if (wfsr==NULL)  { 
    printf("Cannot open writing file: %s\n",fname); 
    return -1;
  } 
  
  // BITMAPFILEHEADER  bfh;   // (14 bytes) 

  bfh.bfType = 'B'|('M'<<8); 
  bfh.bfSize = width * height *4 
              + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPV4HEADER) 
              + sizeof(DWORD)*3; 
  bfh.bfReserved1 = 0; 
  bfh.bfReserved2 = 0; 
  bfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPV4HEADER) 
                + sizeof(DWORD)*3; 
  
  outbytes= fwrite( &bfh, 1, sizeof(BITMAPFILEHEADER), wfsr ); 
  if (outbytes<sizeof(BITMAPFILEHEADER))  { 
    printf("Error outputting file (1): %s\n",fname); 
    fclose(wfsr);  return 1; 
  } 
  
  // BITMAPV4HEADER  bh;    // (108 bytes) 
  
  bh.bV4Size = sizeof(BITMAPV4HEADER); 
  bh.bV4Width = (width); 
  bh.bV4Height = (height); 
  bh.bV4Planes = 1; 
  bh.bV4BitCount = 32; 
  bh.bV4V4Compression = BI_BITFIELDS; 
  bh.bV4SizeImage = 0;  // or = width * height *4; 
  bh.bV4XPelsPerMeter = 0xFF; 
  bh.bV4YPelsPerMeter = 0xFF; 
  bh.bV4ClrUsed = 0; 
  bh.bV4ClrImportant = 0; 
  bh.bV4RedMask   = 0x000000FF; 
  bh.bV4GreenMask = 0x0000FF00; 
  bh.bV4BlueMask  = 0x00FF0000; 
  bh.bV4AlphaMask = 0xFF000000; 
  bh.bV4CSType = LCS_sRGB; 
  bh.bV4Endpoints.ciexyzRed.ciexyzX   = 0;  /* N/A */ 
  bh.bV4Endpoints.ciexyzRed.ciexyzY   = 0; 
  bh.bV4Endpoints.ciexyzRed.ciexyzZ   = 0; 
  bh.bV4Endpoints.ciexyzGreen.ciexyzX = 0; 
  bh.bV4Endpoints.ciexyzGreen.ciexyzY = 0; 
  bh.bV4Endpoints.ciexyzGreen.ciexyzZ = 0; 
  bh.bV4Endpoints.ciexyzBlue.ciexyzX  = 0; 
  bh.bV4Endpoints.ciexyzBlue.ciexyzY  = 0; 
  bh.bV4Endpoints.ciexyzBlue.ciexyzZ  = 0; 
  bh.bV4GammaRed   = 1;  /* N/A */ 
  bh.bV4GammaGreen = 1; 
  bh.bV4GammaBlue  = 1; 
  /* Those values are N/A, because bV4CSType is not LCS_CALIBRATED_RGB. */ 
  
  outbytes= fwrite( &bh, 1, sizeof(BITMAPV4HEADER), wfsr ); 
  if (outbytes<sizeof(BITMAPV4HEADER))  { 
    printf("Error outputting file (2): %s\n",fname); 
    fclose(wfsr);  return 2; 
  } 
  
  // DWORD * 3   // (12 bytes) 

  dsBitfield[0]= 0x000000FF; 
  dsBitfield[1]= 0x0000FF00; 
  dsBitfield[2]= 0x00FF0000; 
  
  outbytes= fwrite( dsBitfield, 1, sizeof(DWORD)*3, wfsr ); 
  if (outbytes<(sizeof(DWORD)*3))  { 
    printf("Error outputting file (3): %s\n",fname); 
    fclose(wfsr);  return 3; 
  } 
  
  // BITMAP data  // ((width * height * 4) bytes)   
  
  for (y=height-1; y>=0 ;y--)  { 
    adr= y * width; 
    for (x=0; x<width ;x++)  { 
         fputc( *(r+adr), wfsr );  // Red 
         fputc( *(g+adr), wfsr );  // Green 
         fputc( *(b+adr), wfsr );  // Blue 
      i= fputc( *(a+adr), wfsr );  // Alpha 
      adr++; 
    } 
    if (i==EOF)  { 
      printf("Error outputting file (4): %s\n",fname); 
      fclose(wfsr);  return 4; 
    } 
  } 
  fclose(wfsr); 
  printf("Saved.\n"); 
  return 0; 
} 


unsigned char  bmpR[96][128];  // 12288 bytes * 4 = 128*96*4  
unsigned char  bmpG[96][128]; 
unsigned char  bmpB[96][128]; 
unsigned char  bmpA[96][128]; 

int main()  { 
  long x,y; 
  float ft; 
  int i; 
  
  // Creating sample image ... 
  for (y=0; y<96 ;y++)  { 
    for (x=0; x<128 ;x++)  { 
      ft= (x-64)*(x-64)+(y-48)*(y-48)*2; 
      ft/= 8; 
      if (ft>255)  ft= 255; 
      bmpR[y][x]= 255-x*2; 
      bmpG[y][x]= 255-y/3*8; 
      bmpB[y][x]= x + y/3*4; 
      bmpA[y][x]= ft; 
    } 
  } 
  
  i= savebmpv4("testv4.bmp", 128, 96, 
                &bmpR[0][0], &bmpG[0][0], &bmpB[0][0], &bmpA[0][0] ); 
  if (i)  { 
    printf("BMP save failed. (%d)\n",i); 
  } else { 
    printf("BMP save OK.\n"); 
  } 
  return i; 
}  


Visual C++ などでこのソースをそのまま用いる場合は、「 Win32 コンソールプログラム 」で新規プロジェクト作成してください。
(If you are using Visual C++ for the above code, we recommend making a new project as a "Win32 Console Program.")

ルーチン savebmpv4( filename, x, y, *R,*G,*B,*A ); は汎用性があり、任意サイズ (x,y) の任意イメージ (R,G,B,A 各8bit) のセーブに使えます。


BITMAPV4 形式 BMP ファイルのオリジナルについて

BITMAPV4 自体は、Windows 95 時代に定義されましたが、
実際に OS 上で読込みがサポートされたのは、Windows 98 以降で、しかも BITMAPV4 の広い定義の内、ごく一部のビット配置のみでした。

上記リスト (Skymaker も同じ) の出力する BITMAPV4 BMP ファイルは、Windows 98 のペイントブラシが出力した BITMAPV4 BMP ファイルフォーマットに倣っています。
具体的には、Windows Update のある時期の Windows 98 のペイントブラシで、5-5-5 bit-field 形式の 16-bbp BMP ファイルを読込ませた時に、ペイントブラシが出力した BMP ファイルが BITMAPV4 形式になっていました。
同様に、BITMAPV5 形式の BMP ファイルも、Skymaker が V2.34 であった時期に、同様の方法でペイントブラシから出力されました。
現在は、このバグは Windows Update でさらに修正されて起こらなくなっているようです。

しかし、Windows XP のペイントブラシでも、同様にして RLE4, RLE8 形式 BMP ファイルが出力できるなど、このバグは Windows XP でも若干残って (潜在して) います。

Skymaker は、この時出力された BITMAPV4, V5 形式のデータ配置が共通していたことから、 これを手本として、透明度付 BMP ファイルを出力しています。


BITMAPV4 形式 BMP ファイル構成について

端的に言うと、Windows 98 の PaintBrush が出力していた BITMAPV4 の BMP ファイルは、 以下の4つのデータを連結して出力したものになっています。
本プログラムは、この4つを順にファイル出力しています。

(1) BITMAPFILEHEADER bfh; // (14 bytes)
(2) BITMAPV4HEADER bh; // (108 bytes)
(3) DWORD dsBitfield[3]; // (12 bytes) // from DIBSECTION?
(4) BitMap data ...

このうち、良く分からないのは、(3) の dsBitfield[] に相当する 3 DWORD のデータがついている事です。
おそらく、DIBSECTION の dsBitfields[] の名残であると推測していますが、
どなたか、正体が分かりましたらご教示下さい。

なお、これら構造体についての Microsoft 社の定義は以下の通りです。

BITMAPFILEHEADER
http://msdn.microsoft.com/en-us/library/dd183374.aspx (=> https://docs.microsoft.com/ja-jp/windows/win32/api/wingdi/ns-wingdi-bitmapfileheader )

BITMAPV4HEADER
http://msdn.microsoft.com/en-us/library/dd183380.aspx (=> https://docs.microsoft.com/ja-jp/windows/win32/api/wingdi/ns-wingdi-bitmapv4header )

DIBSECTION
http://msdn.microsoft.com/en-us/library/dd183567.aspx (=> https://docs.microsoft.com/ja-jp/windows/win32/api/wingdi/ns-wingdi-dibsection )
(2011.2.20 追記:なお、最新の DIBSECTION 構造体では、dshSection と dsOffset が追加されています)
(2021.11.3 リンク先更新)


BITMAPV4 形式 BMP ファイル対応時のコーディングについて

上に述べたように、BITMAPV4 で広く定義されるビット格納形式のうち、実際にはごく一部のビット配列のものしか Windows 95~Me OS 上ではサポートしていません。
具体的には、bV4V4Compression に BI_BITFIELDS が指定されている場合、
bV4RedMask, bV4GreenMask, bV4BlueMask, bV4AlphaMask に従って、 BitMap が読込まれる必要がありますが (Windows 98 Win32 SDK)、
任意のビットフィールドに対応したのは、確認できている範囲で、 Windows XP 以降です。

なお、BMP ファイルとしてではなく、プログラム内部の BMP 転送システムコール (SetDIBits, AlphaBlend 関連) は、XP 以降かなり BITMAPV4 対応できています。
すなわち、SetDIBits( ) で画面表示しているプログラムでは、XP 上では自動的に BITMAPV4 も RGB プレーン表示できますが、 逆に Win Me 以前で実行すると、画面が真っ白となるケースが多く発生します。

OS 側の対応が不完全なので、透明度付 BMP ファイルをどの OS 上でも正常に扱えるようにするには、現状、アプリケーション側でコーディングして対応する必要があります。


BITMAPV4 形式 BMP ファイルのアプリケーション対応について

代表的なアプリケーションとして、ペイントブラシでの表示対応の例を次に示します。
以下は、各 Windows 上のペイント (ペイントブラシ) と Skymaker で、testv4.bmp を読込んだ時の表示例です。

Paint (PaintBrush) Skymaker
Win
95
Windows 95 - PaintBrush


読込みエラーとなります
Windows 95 - Skymaker
Win
XP
Windows XP - Paint


透明度プレーンが無視されます
Windows XP - Skymaker

ペイントでは、Windows 95 から Windows XP 迄に大分 BITMAPV4 の対応が改善されていますが、現状でも透明度プレーンは無視されています。
XP 上の他のアプリケーションでも、SetDIBits で Bitmap 転送しているアプリケーションの多くは、上記 Paint 同様、RGB プレーン表示されているようです。

なお、Skymaker では、OS に依存せずすべて独自処理しているので、Win 95, 98, Me, XP, Mac OS X 上で、みな透明度が処理され、同じ出力画像が得られています。

なお、Imaging では、もう少し早く、Windows 98 以降のものより、BITMAPV4 形式 BMP (RGBプレーンのみの表示) に対応しています。

BITMAPV4 の RGB プレーンが Windows 95~Me でも正常に読込みできるツールとしては、他に、IrfanView、BitMapの極み などがあります。
なお、BitMapの極みでは、透明度対応しており、またソースも公開されていました。
(※2021.11.3 「BitMapの極み」ソース公開サーバ終了)

Microsoft Windows 10 のデスクトップ上及びエクスプローラ上で、2021年2月20日~3月までの間、BITMAPV4 の BMP ファイルが、透明度付で透過表示されていました。 (4月11日までの Windows Update で、RGB プレーン(非透過)の表示へ戻りました。)
(※2021.11.3 追記)



(2011.5.19追記)
(Windows 上) BMP 画像の扱い方 ('Crazy' Y.Sasaki 氏)

いわゆる Windows SDK の標準的な手続きに沿った通常の BMP の読込み/書出し(保存)/画面表示については、こちらのページに詳しいです。
テスト用 BMP ファイルのサンプルもあり、チェックに役立ちます。(※2021.11.3 リンク先サーバ終了)



関連ページ (透明度付 bmp バイナリ)
Related page (with a HEX dump list format)



Last update 2021.11.3

Copyright(c) Finekit 2007, 2021