ぬの部屋(仮)
nu-no-he-ya
  •      12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
         12
    3456789
    10111213141516
    17181920212223
    242526272829 
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    30      
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
        123
    45678910
    11121314151617
    18192021222324
    25262728   
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
       1234
    567891011
    12131415161718
    19202122232425
    2627282930  
           
    1234567
    15161718192021
    293031    
           
         12
    3456789
    10111213141516
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    30      
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
       1234
    567891011
    12131415161718
    19202122232425
    2627282930  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728     
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
          1
    2345678
    9101112131415
    16171819202122
    232425262728 
           
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
  • WindowsからBMPファイルを出力

    BMPファイル形式について

    BMPはMicrosoftとIBMが開発したファイル形式で、Windows版とOS/2版がある。ここではWindows版の32bit,24bitしか考えない。

    以下Wikipedia:

    https://ja.wikipedia.org/wiki/Windows_bitmap

    BMPファイルの4バイト境界について

    BMPファイルは、横一行のデータのバイト数が4の倍数でなければいけないという制約がある。32bit BMPの場合は1画素が4バイトなので必ずこの条件を満たす。従って考える必要はない(考える必要がないだけで制約が取り払われているわけではない)。

    問題は24bitの時で、例えば3×2の画像の一行の画素数は3なので、1行3×(24/8)=9バイトになる。9より大きいもっとも小さな4の倍数は12なので、10,11,12バイト目はダミーのデータで埋めてやる必要がある。

    しかも、32bit BMPは24bit BMPに比べて扱えないソフトが多いとの理由で、.bmp形式なら24bit BMPが世の中では推奨されている。だから24bit BMPを無視できない。

    BMPファイルの出力手順

    1.データが4byte境界の条件を満たしていないなら、満たしているデータを作成する。

    2.BITMAPFILEHEADER構造体に値を設定する

    3.BITMAPINFO構造体に値を設定する

    4.上記2,3をバイナリでファイルに書き出す

    5.画像データをファイルに書き出す

    BMPファイル形式はMicrosoftが考案した形式だが、Windowsに用意されているのはヘッダの構造体だけだ。データの4byte境界を修正する関数はおろかそのエラーチェックをする関数も用意されていない。圧縮もサポートしていると言ってもヘッダに圧縮状態のフラグを指定できるだけで、使いたかったら自分で圧縮しなければならない。ヘッダの出力もやってることはただのfwriteで、sizeof(BITMAPFILEHEADER)バイトだけ、sizeof(BITMAPINFO)バイトだけバイナリ出力しているに過ぎない。

    サンプルコード

    BMPwriter.hpp

    #pragma once
    
    //! @brief 与えられた画素数がビットマップの要求する4byte境界に何バイト足りないかを返す
    //! @param [in] width 画素数
    //! @param [in] pixelsize 1ピクセルのビット数
    //! @return 足して4の倍数になる値
    int getbmp4line_err(const unsigned int width, const unsigned int pixelbits);
    
    //! @brief 幅と高さと画素のビット数からビットマップデータにしたときに何バイトになるかを算出する
    //! @param [in] width 画像のピクセル数(幅)
    //! @param [in] height 画像のピクセル数(高さ)
    //! @param [in] pixelbit 1ピクセルのビット数
    //! @return 4バイト境界に合わせたときのバイト数
    unsigned int getbmpdata_memsize(const unsigned int width, const unsigned int height, const unsigned int pixelbits);
    
    
    //! @brief データをスキャンライン4倍バイトデータにしたものを作成
    //! @param [out] dst 調整後の画素データの格納先アドレス
    //! @param [in] src 元データの先頭アドレス
    //! @param [in] width 画像幅(ピクセル)
    //! @param [in] height 画像高さ(ピクセル)
    //! @param [in] pixelbit 1ピクセルのビット数
    //! @return そのままBMPにできるデータ配列
    //! @note dstは確保済みの必要がある。このバイト数はgetbmpdatasizeで取得できる
    void data_to_bmp4lines(unsigned char* dst,const void* src, const unsigned int width, const unsigned int height, const unsigned int pixelbits);
    
    //! @brief ビットマップファイルを保存する
    //! @param [in] pathname ファイル名
    //! @param [in] pixeldata 画像データの先頭ポインタ。4バイト境界に調整済みの必要がある
    //! @param [in] width 画像のピクセル幅
    //! @param [in] height 画像のピクセル高さ
    //! @param [in] 1画素のビット数
    //! @return なし
    void bmp_write(const wchar_t* pathname, const void* pixeldata, const unsigned int width, const unsigned int height, const unsigned int pixelbits);
    
    //! @brief ビットマップファイルを保存する関数のインタフェース
    //! @param [in] pathname ファイル名
    //! @param [in] pixeldata 画像データの先頭ポインタ。4バイト境界の調整前のもの
    //! @param [in] width 画像のピクセル幅
    //! @param [in] height 画像のピクセル高さ
    //! @param [in] 1画素のビット数
    //! @return なし
    void call_bmp_write(const wchar_t* pathname, const void* pixeldata, const unsigned int width, const unsigned int height, const unsigned int pixelbits);
    

    BMPWriter.cpp

    #include "BMPwriter.hpp"
    
    #include <Windows.h>
    #include <vector>
    
    
    
    int getbmp4line_err(const unsigned int width, const unsigned int pixelbits) {
      return  (4 - (width * pixelbits /8) % 4) % 4;
    }
    unsigned int getbmpdata_memsize(const unsigned int width, const unsigned int height, const unsigned int pixelbits) {
      unsigned int widthbytes = width * (pixelbits / 8) + getbmp4line_err(width, pixelbits);
      return widthbytes * height;
    }
    void data_to_bmp4lines(unsigned char* dst, const void* src, const unsigned int width, const unsigned int height, const unsigned int pixelbits) {
      unsigned int pixelbytes = (pixelbits / 8);
      std::uint8_t * p = (std::uint8_t*)src;
      int err = getbmp4line_err(width, pixelbits);//4バイト境界に何バイト足りないかを算出
    
      size_t pos=0;
      for (size_t h = 0; h < height; h++) {
        //一行コピー
        for (size_t x = 0; x < width * pixelbytes; x++) {
          dst[pos++] = *p;
          p++;
        }
        if (err != 0) {
          // width * pixelsizeが4になるようにデータを挿入
          for (size_t k = 0; k < err; k++) {
            dst[pos++] = 0;
          }
        }
      }
    
    }
    void bmp_write(
      const wchar_t* pathname,
      const void* pixeldata, 
      const unsigned int width,
      const unsigned int height,
      const unsigned int pixelbits) {
    
      const unsigned long bytecount = pixelbits / 8;
    
      const unsigned long    headSize = (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFO));
    
      //4バイト境界を考慮した1スキャンラインのバイト数
      const unsigned long widthBytes = width * bytecount + getbmp4line_err(width, pixelbits);
    
      const unsigned long    imageBytes = (widthBytes * height);
    
      // BITMAPFILEHEADERの初期化
      BITMAPFILEHEADER bmpHead = { 0 };
      bmpHead.bfType = 0x4D42;       // "BM"
      bmpHead.bfSize = headSize + imageBytes;
      bmpHead.bfReserved1 = 0;
      bmpHead.bfReserved2 = 0;
      bmpHead.bfOffBits = headSize;
    
      // BITMAPINFOの初期化
      BITMAPINFO bmpInfo = { 0 };
      bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
      bmpInfo.bmiHeader.biWidth = width;
      bmpInfo.bmiHeader.biHeight = height;
      bmpInfo.bmiHeader.biPlanes = 1;
      bmpInfo.bmiHeader.biBitCount = pixelbits;
      bmpInfo.bmiHeader.biCompression = BI_RGB;
      bmpInfo.bmiHeader.biSizeImage = 0;
      bmpInfo.bmiHeader.biXPelsPerMeter = 0;
      bmpInfo.bmiHeader.biYPelsPerMeter = 0;
      bmpInfo.bmiHeader.biClrUsed = 0;
      bmpInfo.bmiHeader.biClrImportant = 0;
    
    
      FILE * fp = _wfopen(pathname, L"wb" );
    
      fwrite(&bmpHead, sizeof(BITMAPFILEHEADER), 1, fp);
      fwrite(&bmpInfo, sizeof(BITMAPINFO), 1, fp);
    
      fwrite(pixeldata, 1 , imageBytes, fp);
    
      fclose(fp);
    
    }
    void call_bmp_write(const wchar_t* pathname, const void* pixeldata, const unsigned int width, const unsigned int height, const unsigned int pixelbits) {
    
      //BMP保存時の画像のメモリサイズを求める
      int retsize = getbmpdata_memsize(width, height, pixelbits);
      std::vector<std::uint8_t> save;
      save.resize(retsize);
    
      //4バイト境界の条件を満たしたデータを作成する
      data_to_bmp4lines(&save[0], pixeldata, width, height, pixelbits);
    
      //BMPファイルに保存する
      bmp_write(pathname, &save[0], width, height, pixelbits);
    }
    

    使用例

    #include <iostream>
    #include <vector>
    
    #include "BMPwriter.hpp"
    
    struct data32bit {
      unsigned char p[4];
      data32bit() {}
      data32bit(
        unsigned char b,
        unsigned char g,
        unsigned char r,
        unsigned char u
      ) {
        p[0] = b;
        p[1] = g;
        p[2] = r;
        p[3] = u;
      }
      data32bit(
        unsigned char b,
        unsigned char g,
        unsigned char r
      ) {
        p[0] = b;
        p[1] = g;
        p[2] = r;
        p[3] = 0;
      }
    };
    struct data24bit {
      unsigned char p[3];
      data24bit() {}
      data24bit(
        unsigned char b,
        unsigned char g,
        unsigned char r
      ) {
        p[0] = b;
        p[1] = g;
        p[2] = r;
      }
    };
    
    template<typename dtype>
    void create_data(std::vector<dtype>& img,int width,int height) {
      for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++) {
          int pos = y * width + x;
          if (y == x) {
            img[pos] = dtype(255, 0, 0);
          }
          else {
            img[pos] = dtype(0, 0, 255);
          }
          if (x == width - 1)
            img[pos] = dtype(0, 255, 0);
        }
      }
    }
    
    
    int main()
    {
    
      //画像データ
      std::vector<data24bit> img;
      unsigned int bits = 24;
      unsigned int width = 7;
      unsigned int height = 9;
      img.resize(width*height);
      create_data(img,width,height);
    
      std::cout << "scanline bytes % 4 = " << (width * bits / 8) % 4;
    
      call_bmp_write(L"C:\\data\\a.bmp",&img[0], width, height, bits);
    
      int k;
      std::cin >> k;
    
    }