ぬの部屋(仮)
nu-no-he-ya
  • 1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    30      
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
         12
    3456789
    10111213141516
    17181920212223
    2425262728  
           
      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
           
       1234
    567891011
    12131415161718
    19202122232425
    26272829   
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
       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     
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
       1234
    567891011
    12131415161718
    19202122232425
    2627282930  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
     123456
    78910111213
    14151617181920
    21222324252627
    28      
           
         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     
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
       1234
    567891011
    12131415161718
    19202122232425
    2627282930  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
    1234567
    891011121314
    15161718192021
    22232425262728
           
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    30      
       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     
         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
           
  • FreeType2を試す-2 文字列を描画してみる

    #include <string>
    #include <array>
    
    #include <ft2build.h>
    #include FT_FREETYPE_H
    
    #pragma warning(disable:4996)
    
    #pragma comment(lib,"freetyped.lib")
    
    
    const int imageWidth = 1000;
    const int imageHeight = 300;
    std::array<unsigned char, imageWidth* imageHeight> image;
    int pixel_pos(const int x, const int y) {
      return y * imageWidth + x;
    }
    
    
    void draw(const FT_Bitmap& bmp,int startx, int starty) {
    
      int Width = bmp.width;
      int Height = bmp.rows;
    
      for (size_t y = 0; y < Height; y++) {
        for (size_t x = 0; x < Width; x++) {
    
          int xx = startx + x;
          int yy = starty + y;
    
          if (xx < 0)continue;
          if (yy < 0)continue;
    
          if (bmp.buffer[y*Width+x]) {
            image[pixel_pos(xx, yy)] = 1;
          }
        }
      }
    
    }
    //! @brief PBM(1byte,テキスト)を書き込む
    //! @param [in] fname ファイル名
    //! @param [in] width 画像の幅
    //! @param [in] height 画像の高さ
    //! @param [in] p 画像のメモリへのアドレス
    //! @details 1画素1Byteのメモリを渡すと、0,1テキストでファイル名fnameで書き込む
    void pbmP1_Write(const char* const fname, const int width, const int height, const unsigned char* const p) { // PPM ASCII
    
      FILE* fp = fopen(fname, "wb");
      fprintf(fp, "P1\n%d\n%d\n", width, height);
    
      size_t k = 0;
      for (size_t i = 0; i < (size_t)height; i++) {
        for (size_t j = 0; j < (size_t)width; j++) {
          fprintf(fp, "%d ", p[k]?0:1);
          k++;
        }
        fprintf(fp, "\n");
      }
    
      fclose(fp);
    }
    int main()
    {
    
      FT_Library  library; // handle to library
      FT_Error error;
      
      error = FT_Init_FreeType(&library);
      if (error)
        return -1;
    
      FT_Face face;      // handle to face object
    
      // フォントファイル読み込み
      error = FT_New_Face(
        library,
        "C:\\Windows\\Fonts\\meiryo.ttc",
        0,
        &face
      );
    
      //文字コード指定
      error = FT_Select_Charmap(
        face,               // target face object
        FT_ENCODING_UNICODE // エンコード指定
      );
    
    
      if (error == FT_Err_Unknown_File_Format)
        return -1;
      else if (error)
        return -1;
    
      //この二つの値でフォントサイズ調整
      FT_F26Dot6 fontsize=16*64;
      FT_UInt CHAR_RESOLUTION = 300;
      error = FT_Set_Char_Size(
        face,                // handle to face object
        0,                   // char_width in 1/64th of points
        fontsize,            // char_height in 1/64th of points
        CHAR_RESOLUTION,     // horizontal device resolution
        CHAR_RESOLUTION);    // vertical device resolution
    
    
      int pen_x=0;
      int pen_y=0;
    
    
      std::wstring chars = L"いろ は。にほへと,abc d";
      for (size_t k = 0; k < chars.size(); k++) {
    
        // 文字の取得
        FT_ULong character = chars[k];
        FT_UInt char_index = FT_Get_Char_Index(face, character);
    
        // グリフ(字の形状)読込
        error = FT_Load_Glyph(face, char_index, FT_LOAD_RENDER);
        if (error)
          return -1; // ignore errors
    
        // 文字を画像化
        FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
    
    
        // 画像書き込み
        draw(
          face->glyph->bitmap,
          pen_x + face->glyph->bitmap_left,
          pen_y - face->glyph->bitmap_top + 100
        );
        // 描画位置を更新
        pen_x += face->glyph->advance.x >> 6;
        pen_y += face->glyph->advance.y >> 6; // not useful for now
      }

      // ファイル書き込み
      pbmP1_Write(
        "C:\\test\\freetypetest.pbm",
        imageWidth,
        imageHeight,
        &image[0]
      );
    
      // FreeType2の解放
      FT_Done_Face(face);
      FT_Done_FreeType(library);
    }
    

    出力結果

    ピクセル数で指定

      /*
      //この二つの値でフォントサイズ調整
      FT_F26Dot6 fontsize = 16 * 64;
      FT_UInt CHAR_RESOLUTION = 300;
      error = FT_Set_Char_Size(
        face,                // handle to face object
        0,                   // char_width in 1/64th of points
        fontsize,            // char_height in 1/64th of points
        CHAR_RESOLUTION,     // horizontal device resolution
        CHAR_RESOLUTION);    // vertical device resolution
      */
    
      FT_Set_Pixel_Sizes(
        face,     // handle to face object
        0,        // pixel_width  
        64        // pixel_height
      );
    

    FreeType2を試す-1-一文字だけ出力

    まず一文字だけpbmファイル出力するプログラムが以下。

    #include <ft2build.h>
    #include FT_FREETYPE_H
    
    #pragma warning(disable:4996)
    
    #pragma comment(lib,"freetype.lib")
    
    
    //! @brief PBM(1byte,テキスト)を書き込む
    //! @param [in] fname ファイル名
    //! @param [in] width 画像の幅
    //! @param [in] height 画像の高さ
    //! @param [in] p 画像のメモリへのアドレス
    //! @details 1画素1Byteのメモリを渡すと、0,1テキストでファイル名fnameで書き込む
    void pbmP1_Write(const char* const fname, const int width, const int height, const unsigned char* const p) { // PPM ASCII
    
      FILE* fp = fopen(fname, "wb");
      fprintf(fp, "P1\n%d\n%d\n", width, height);
    
      size_t k = 0;
      for (size_t i = 0; i < (size_t)height; i++) {
        for (size_t j = 0; j < (size_t)width; j++) {
          fprintf(fp, "%d ", p[k]?0:1);
          k++;
        }
        fprintf(fp, "\n");
      }
    
      fclose(fp);
    }
    int main()
    {
    
      FT_Library  library; // handle to library
      FT_Error error;
      
      error = FT_Init_FreeType(&library);
      if (error)
        return -1;
    
      FT_Face face;      // handle to face object
    
      // フォントファイル読み込み
      error = FT_New_Face(
        library,
        "C:\\Windows\\Fonts\\meiryo.ttc",
        0,
        &face
      );
    
      //文字コード指定
      error = FT_Select_Charmap(
        face,               // target face object
        FT_ENCODING_UNICODE // エンコード指定
      );
    
    
      if (error == FT_Err_Unknown_File_Format)
        return -1;
      else if (error)
        return -1;
    
      //この二つの値でフォントサイズ調整
      FT_F26Dot6 fontsize=16*64;
      FT_UInt CHAR_RESOLUTION = 300;
      error = FT_Set_Char_Size(
        face,                // handle to face object
        0,                   // char_width in 1/64th of points
        fontsize,            // char_height in 1/64th of points
        CHAR_RESOLUTION,     // horizontal device resolution
        CHAR_RESOLUTION);    // vertical device resolution
    
      // 文字の取得
      FT_ULong character = wchar_t(L'あ');
      FT_UInt char_index = FT_Get_Char_Index(face, character);
    
      // グリフ(字の形状)読込
      error = FT_Load_Glyph(face, char_index, FT_LOAD_RENDER);
      if (error)
        return -1; // ignore errors
    
      // 文字を画像化
      FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
    
      int Width = face->glyph->bitmap.width;
      int Height = face->glyph->bitmap.rows;
      pbmP1_Write(// ファイル保存
        "C:\\test\\freetypetest.pbm", 
        Width, 
        Height, 
        face->glyph->bitmap.buffer
      );
    
      // FreeType2の解放
      FT_Done_Face(face);
      FT_Done_FreeType(library);
    }
    

    出力結果

    PNM Writer(改)

    PNMの書き込み関数を書いた。

    ただし前回のPNM Readerで書いたPNM_TYPE列挙体が共通のため、前回のPNM Readerと同じファイルに書くことが望ましい。

    プログラム本体(含 PNM Reader)

    #pragma once
    
    #include <fstream>
    #include <sstream>
    #include <string>
    #include <vector>
    #include <algorithm>
    
    
    //! @brief PNMファイルの種類
    enum class PNM_TYPE {
      P1 = 1,  //Portable bitmap   ASCII
      P2 = 2,  //Portable graymap  ASCII
      P3 = 3,  //Portable pixmap   ASCII
      P4 = 4,  //Portable bitmap   Binary
      P5 = 5,  //Portable graymap  Binary
      P6 = 6,  //Portable pixmap   Binary
      PError = 7,
      PBitmap = 8,
      PGraymap = 9,
      PPixmap = 10
    };
    //////////////////////////////////////////////////////////
    // PNM Reader
    
    
    //! @class CGetGrayBits //! @brief 配列上のx番目の画素値を取得するクラス //! @sa https://suzulang.com/bit-depth-1-2-4-getpixelvalue/ class CGetGrayBits { unsigned char* m_p; //!< 画素へのポインタ const int m_depth = 1; //!< ビット深度 unsigned char m_mask = 1; //!< depth8未満の時のビットマスク public: //! @brief コンストラクタ //! @param [in] p 画素配列へのポインタ CGetGrayBits( const void* const p) : m_p(static_cast<unsigned char*>(const_cast<void*>(p))) { } //! @brief 指定した番号の画素を取得 //! @param [in] x 画素番号 //! @return 画素値 int operator[](const std::size_t x) { // x ドットが何バイト目かを算出 (0開始) std::size_t byte = x / (8 / m_depth); unsigned char c = m_p[byte]; std::size_t pos = x % (8 / m_depth); int shift = 8 - m_depth - pos * m_depth; unsigned char mask1 = m_mask << shift; unsigned int val = (c & mask1) >> shift; return val; } }; //! @brief ストリームから入力 bitmap ASCII //! @param [out] img 画像の格納先 //! @param [out] s 入力ストリーム void read_P1(std::vector<unsigned char>& img, std::ifstream& s) { while (s.eof() == false) { char c; s.read(&c, 1); if (isdigit(c) != false) { img.push_back(c - '0'); } } } //! @brief ストリームから入力 graymap,pixmap ASCII //! @param [out] img 画像の格納先 //! @param [out] s 入力ストリーム void read_P23(std::vector<unsigned char> & img, std::ifstream & s) { while (s.eof() == false) { std::string val; s >> val; int value; try { value = std::stoi(val); } catch (std::invalid_argument) { continue; } img.push_back(value); } } //! @brief ストリームから入力 bitmap Binary //! @param [out] img 画像の格納先 //! @param [out] s 入力ストリーム //! @return なし //! @details P4は1ビット=1画素なので全部読み込んでから1ビットずつとりだしている void read_P4(std::vector<unsigned char> & img, std::ifstream & s, int count) { std::vector<unsigned char> tmp; unsigned char tc; while (!s.eof()) { s.read((char*)& tc, sizeof(char)); tmp.push_back(tc); } CGetGrayBits bits(&tmp[0]); for (size_t i = 0; i < count; i++) { img.push_back(bits[i]); } } //! @brief ストリームから入力 graymap,pixmap Binary //! @param [out] img 画像の格納先 //! @param [out] s 入力ストリーム //! @return なし void read_P56(std::vector<unsigned char> & img, std::ifstream & s) { while (s.eof() == false) { unsigned char c; s.read((char*)& c, 1); img.push_back(c); } } //! @brief pnmファイルを読み込む //! @param [in] fpathname pnmファイルへのパス //! @param [out] img 画像の格納先 //! @param [out] width 横方向の画素数 //! @param [out] height 縦方向の画素数 //! @param [out] maxvalue 画素の最大値 //! @param [out] datatype 形式を識別する定数 //! @retval true 読込成功 //! @retval false 読込失敗 bool read_pnm(const char* fpathname, std::vector<unsigned char> * img, int* width, int* height, int* maxvalue, PNM_TYPE * datatype) { std::ifstream ifs(fpathname, std::ios::in | std::ios::binary); if (!ifs) return false; std::string text; *width = -1; *height = -1; *maxvalue = -1; //タイプの読み取り PNM_TYPE type; { int _type = (int)PNM_TYPE::PError; while (std::getline(ifs, text).eof() == false) { if (text[0] == 'P') { _type = ((int)text[1] - (int)'0'); break; } } switch (_type) { case (int)PNM_TYPE::P1:*datatype = PNM_TYPE::PBitmap; break; case (int)PNM_TYPE::P2:*datatype = PNM_TYPE::PGraymap; break; case (int)PNM_TYPE::P3:*datatype = PNM_TYPE::PPixmap; break; case (int)PNM_TYPE::P4:*datatype = PNM_TYPE::PBitmap; break; case (int)PNM_TYPE::P5:*datatype = PNM_TYPE::PGraymap; break; case (int)PNM_TYPE::P6:*datatype = PNM_TYPE::PPixmap; break; default: *datatype = PNM_TYPE::PError; return false; } type = (PNM_TYPE)_type; } while (std::getline(ifs, text).eof() == false) { //コメントスキップ if (text[0] == '#') continue; //コメントでないなら各種値の入力 std::stringstream ss(text); //幅入力 if (*width < 0) { ss >> *width; if (*width < 0) continue; } //高さ入力 if (*height < 0) { ss >> *height; if (*height < 0) continue; } //最大値入力 if (*datatype != PNM_TYPE::PBitmap) { if (*maxvalue < 0) { ss >> *maxvalue; if (*maxvalue < 0) continue; } } break; } int count = *width * *height; switch (type) { case PNM_TYPE::P1: img->reserve(count); read_P1(*img, ifs); break; case PNM_TYPE::P2: img->reserve(count); read_P23(*img, ifs); break; case PNM_TYPE::P3: img->reserve(count * 3); read_P23(*img, ifs); break; case PNM_TYPE::P4: read_P4(*img, ifs, count); break; case PNM_TYPE::P5: read_P56(*img, ifs); break; case PNM_TYPE::P6: read_P56(*img, ifs); break; } }
    //
    //////////////////////////////////////////////////////////
    
    //////////////////////////////////////////////////////////
    // PNM Writer
    
    
    //! @brief 1byteの各ビットに配列のようにアクセスするためのクラス
    class bititerator_t {
      unsigned char* m_c;
      int m_index;
    public:
      bititerator_t(unsigned char* c, const int index) {
        m_c = c;
        m_index = 7 - index;
      }
      void operator=(const int value) {
        unsigned char mask = (1 << m_index);
    
        if (value) {
          *m_c |= mask;
        }
        else {
          *m_c &= ~mask;
        }
      }
      operator unsigned char() {
        unsigned char mask = (1 << m_index);
    
        return (*m_c & mask) ? 1 : 0;
      }
    
    
    };
    
    //! @brief 1byteの各ビットに配列のようにアクセスするためのクラス
    class BitArray {
      unsigned char c;
    public:
      BitArray() :c(0) {}
      bititerator_t operator[](const int i) {
        return bititerator_t(&c, i);
      }
      operator unsigned char() {
        return c;
      }
      unsigned char operator=(const unsigned char value) {
        c = value;
        return c;
      }
    };
    //////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////
    
    
    
    //! @brief データタイプを文字列に変換
    std::string to_string(const PNM_TYPE type) {
      switch (type) {
      case PNM_TYPE::P1:return "P1"; break;
      case PNM_TYPE::P2:return "P2"; break;
      case PNM_TYPE::P3:return "P3"; break;
      case PNM_TYPE::P4:return "P4"; break;
      case PNM_TYPE::P5:return "P5"; break;
      case PNM_TYPE::P6:return "P6"; break;
      }
      return "";
    
    }
    
    //! @brief 各データタイプにおける1ピクセルのビット数を求める
    int pixel_bit_count(const PNM_TYPE type) {
      switch (type) {
      case PNM_TYPE::P1:return 1;   break;
      case PNM_TYPE::P2:return 8;   break;
      case PNM_TYPE::P3:return 8 * 3; break;
      case PNM_TYPE::P4:return 1;   break;
      case PNM_TYPE::P5:return 8;   break;
      case PNM_TYPE::P6:return 8 * 3; break;
      }
      return -1;
    
    }
    //! @brief Portable bitmap   ASCII
    //! @param [in,out] s 出力先
    //! @param [in] img 1byte1画素の画像データ
    //! @param [in] width 幅画素数
    //! @param [in] height 高さ画素数
    //! @return なし
    void write_P1(std::ofstream& s, const unsigned char* img, const int width, const int height) {
      int count = width * height;
      for (size_t i = 0; i < count; i++) {
        s << std::to_string(img[i] ? 1 : 0);
      }
    }
    //! @brief ストリームへ出力 Portable graymap  ASCII
    //! @param [in,out] 出力先
    //! @param [in] img 画像データ
    //! @param [in] width 画素数幅
    //! @param [in] height 画素数高さ
    //! @return なし
    void write_P2(std::ofstream & s, const unsigned char* img, const int width, const int height) {
      int count = width * height;
      int i = 0;
      for (size_t h = 0; h < height; h++) {
        for (size_t w = 0; w < width; w++) {
          s << std::to_string(img[i]) << " ";
          i++;
        }
        s << std::endl;
      }
    }
    //! @brief ストリームへ出力 Portable pixmap   ASCII
    //! @param [in,out] 出力先
    //! @param [in] img 画像データ
    //! @param [in] width 画素数幅
    //! @param [in] height 画素数高さ
    //! @return なし
    void write_P3(std::ofstream & s, const unsigned char* img, const int width, const int height) {
      int count = width * height;
      int i = 0;
      for (size_t h = 0; h < height; h++) {
        for (size_t w = 0; w < width; w++) {
          s << std::to_string(img[i * 3 + 0]) << " ";
          s << std::to_string(img[i * 3 + 1]) << " ";
          s << std::to_string(img[i * 3 + 2]) << " ";
          i++;
        }
        s << std::endl;
      }
    }
    //! @brief ストリームへ出力 Portable bitmap   Binary
    //@details 入力が1byte 1pixel なので、 8要素を1byteにまとめる
    //! @param [in,out] 出力先
    //! @param [in] img 画像データ
    //! @param [in] count 画素数
    //! @return なし
    void write_P4(std::ofstream & s, const unsigned char* img, const int count) {
    
      int bitindex = 0;
      BitArray bits;
      for (size_t i = 0; i < count; i++) {
    
        if (bitindex == 0) {
          bits = 0;
        }
    
        bits[bitindex] = img[i];
    
        bitindex++;
        bitindex %= 8;
    
        if (bitindex == 0) {
          unsigned char c = bits;
          s.write((const char*)& c, 1);
        }
      }
    
    }
    //! @brief ストリームへ出力 Portable graymap  Binary , Portable pixmap   Binary
    //@details 入力が1byte 1pixel なので、 8要素を1byteにまとめる
    //! @param [in,out] 出力先
    //! @param [in] img 画像データ
    //! @param [in] bytes 画像データのバイト数
    //! @return なし
    void write_P56(std::ofstream & s, const unsigned char* img, const int bytes) {
      s.write((char*)img, bytes);
    }
    //! @brief pnmファイルを書き込む
    //! @param [in] fpathname pnmファイルへのパス
    //! @param [in] img 画像の格納先
    //! @param [int] width 横方向の画素数
    //! @param [in] height 縦方向の画素数
    //! @param [in] datatype 形式を識別、指定する定数
    //! @retval true 書き込み成功
    //! @retval false 書き込み失敗
    bool write_pnm(const char* fpathname, const unsigned char* img, const int width, const int height, const PNM_TYPE datatype) {
    
      if (datatype >= PNM_TYPE::PError)
        return false;
    
      int count = width * height;
      if (count <= 0)
        return false;
    
      std::ofstream ofs(fpathname, std::ios::out | std::ios::binary);
      if (!ofs)
        return false;
    
    
      int pixelbit = pixel_bit_count(datatype);
    
    
      ofs << to_string(datatype) << std::endl;
      ofs << width << " " << height << std::endl;
    
      int maxvalue = -1;
      if ((datatype != PNM_TYPE::P1) && (datatype != PNM_TYPE::P4)) {
        maxvalue = *std::max_element(
          img,
          img + count * pixel_bit_count(datatype) / 8);
    
        ofs << maxvalue << std::endl;
      }
    
    
      switch (datatype) {
      case PNM_TYPE::P1:
        write_P1(ofs, img, width, height);
        break;
      case PNM_TYPE::P2:
        write_P2(ofs, img, width, height);
        break;
      case PNM_TYPE::P3:
        write_P3(ofs, img, width, height);
        break;
      case PNM_TYPE::P4:
        write_P4(ofs, img, width * height);
        break;
      case PNM_TYPE::P5:
        write_P56(ofs, img, width * height);
        break;
      case PNM_TYPE::P6:
        write_P56(ofs, img, width * height * 3);
        break;
      }
    }
    

    テストプログラム

    #include <iostream>
    
    #include "pnm_rw.hpp"
    
    
    int main()
    {
      const char* fpathname = "C:\\test\\butterfly-5546907_640-ascii.pbm";
    
    
      std::vector<unsigned char> img;
      int width, height, maxvalue;
      PNM_TYPE type;
      read_pnm(
        fpathname,
        &img,
        &width,
        &height,
        &maxvalue,
        &type
      );
    
    
      switch (type) {
    
      case PNM_TYPE::PBitmap:
        write_pnm("C:\\test\\out-ascii.pbm", img.data(), width, height, PNM_TYPE::P1);
        write_pnm("C:\\test\\out-binary.pbm", img.data(), width, height, PNM_TYPE::P4);
        break;
      case PNM_TYPE::PGraymap:
        write_pnm("C:\\test\\out-ascii.pgm", img.data(), width, height, PNM_TYPE::P2);
        write_pnm("C:\\test\\out-binary.pgm", img.data(), width, height, PNM_TYPE::P5);
        break;
      case PNM_TYPE::PPixmap:
        write_pnm("C:\\test\\out-ascii.ppm", img.data(), width, height, PNM_TYPE::P3);
        write_pnm("C:\\test\\out-binary.ppm", img.data(), width, height, PNM_TYPE::P6);
        break;
      }
    }
    

    PNM Reader(改)

    プログラム本体(pnm_reader.hpp)

    #pragma once
    
    #include <fstream>
    #include <sstream>
    #include <string>
    #include <vector>
    
    #pragma warning(disable:4996)
    
    
    
    //! @class CGetGrayBits
    //! @brief 配列上のx番目の画素値を取得するクラス
    //! @sa https://suzulang.com/bit-depth-1-2-4-getpixelvalue/
    class CGetGrayBits {
      unsigned char* m_p;        //!< 画素へのポインタ
      const int m_depth = 1;     //!< ビット深度
    
      unsigned char m_mask = 1;  //!< depth8未満の時のビットマスク
    public:
    
      //! @brief コンストラクタ
      //! @param [in] p 画素配列へのポインタ
      CGetGrayBits(
        const void* const p) :
        m_p(static_cast<unsigned char*>(const_cast<void*>(p))) {
      }
    
      //! @brief 指定した番号の画素を取得
      //! @param [in] x 画素番号
      //! @return 画素値
      int operator[](const std::size_t x) {
        // x ドットが何バイト目かを算出 (0開始)
        std::size_t byte = x / (8 / m_depth);
    
        unsigned char c = m_p[byte];
    
        std::size_t pos = x % (8 / m_depth);
    
        int shift = 8 - m_depth - pos * m_depth;
    
        unsigned char mask1 = m_mask << shift;
        unsigned int val = (c & mask1) >> shift;
    
        return val;
      }
    
    };
    //! @brief PNMファイルの種類
    enum class PNM_TYPE {
      P1 = 1,  //Portable bitmap   ASCII
      P2 = 2,  //Portable graymap  ASCII
      P3 = 3,  //Portable pixmap   ASCII
      P4 = 4,  //Portable bitmap   Binary
      P5 = 5,  //Portable graymap  Binary
      P6 = 6,  //Portable pixmap   Binary
      PError = 7,
      PBitmap = 8,
      PGraymap = 9,
      PPixmap = 10
    };
    //! @brief ストリームから入力 bitmap ASCII
    //! @param [out] img 画像の格納先
    //! @param [out] s 入力ストリーム
    void read_P1(std::vector<unsigned char>& img, std::ifstream& s) {
      while (s.eof() == false) {
    
        char c;
        s.read(&c, 1);
        if (isdigit(c) != false) {
          img.push_back(c - '0');
        }
    
      }
    
    }
    //! @brief ストリームから入力 graymap,pixmap ASCII
    //! @param [out] img 画像の格納先
    //! @param [out] s 入力ストリーム
    void read_P23(std::vector<unsigned char> & img, std::ifstream & s) {
      while (s.eof() == false) {
    
        std::string val;
    
        s >> val;
    
        int value;
        try {
          value = std::stoi(val);
        }
        catch (std::invalid_argument) {
          continue;
        }
    
        img.push_back(value);
    
      }
    
    }
    //! @brief ストリームから入力 bitmap Binary
    //! @param [out] img 画像の格納先
    //! @param [out] s 入力ストリーム
    //! @return なし
    //! @details P4は1ビット=1画素なので全部読み込んでから1ビットずつとりだしている
    void read_P4(std::vector<unsigned char> & img, std::ifstream & s, int count) {
      std::vector<unsigned char> tmp;
    
      unsigned char tc;
      while (!s.eof()) {
        s.read((char*)& tc, sizeof(char));
        tmp.push_back(tc);
      }
    
      CGetGrayBits bits(&tmp[0]);
    
      for (size_t i = 0; i < count; i++) {
        img.push_back(bits[i]);
      }
    
    }
    //! @brief ストリームから入力 graymap,pixmap Binary
    //! @param [out] img 画像の格納先
    //! @param [out] s 入力ストリーム
    //! @return なし
    void read_P56(std::vector<unsigned char> & img, std::ifstream & s) {
      while (s.eof() == false) {
    
        unsigned char c;
        s.read((char*)& c, 1);
        img.push_back(c);
    
      }
    }
    //! @brief pnmファイルを読み込む
    //! @param [in] fpathname pnmファイルへのパス
    //! @param [out] img 画像の格納先
    //! @param [out] width 横方向の画素数
    //! @param [out] height 縦方向の画素数
    //! @param [out] maxvalue 画素の最大値
    //! @param [out] datatype 形式を識別する定数
    //! @retval true 読込成功
    //! @retval false 読込失敗
    bool read_pnm(const char* fpathname, std::vector<unsigned char> * img, int* width, int* height, int* maxvalue, PNM_TYPE * datatype) {
      std::ifstream ifs(fpathname, std::ios::in | std::ios::binary);
    
      if (!ifs)
        return false;
    
      std::string text;
    
      *width = -1;
      *height = -1;
      *maxvalue = -1;
    
      //タイプの読み取り
      PNM_TYPE type;
      {
        int _type = (int)PNM_TYPE::PError;
        while (std::getline(ifs, text).eof() == false) {
          if (text[0] == 'P') {
            _type = ((int)text[1] - (int)'0');
            break;
          }
        }
        switch (_type) {
        case (int)PNM_TYPE::P1:*datatype = PNM_TYPE::PBitmap; break;
        case (int)PNM_TYPE::P2:*datatype = PNM_TYPE::PGraymap; break;
        case (int)PNM_TYPE::P3:*datatype = PNM_TYPE::PPixmap; break;
        case (int)PNM_TYPE::P4:*datatype = PNM_TYPE::PBitmap; break;
        case (int)PNM_TYPE::P5:*datatype = PNM_TYPE::PGraymap; break;
        case (int)PNM_TYPE::P6:*datatype = PNM_TYPE::PPixmap; break;
        default:
          *datatype = PNM_TYPE::PError;
          return false;
        }
        type = (PNM_TYPE)_type;
      }
    
      while (std::getline(ifs, text).eof() == false) {
    
        //コメントスキップ
        if (text[0] == '#')
          continue;
    
        //コメントでないなら各種値の入力
        std::stringstream ss(text);
    
        //幅入力
        if (*width < 0) {
          ss >> *width;
          if (*width < 0)
            continue;
        }
    
        //高さ入力
        if (*height < 0) {
          ss >> *height;
          if (*height < 0)
            continue;
        }
    
        //最大値入力
        if (*datatype != PNM_TYPE::PBitmap) {
          if (*maxvalue < 0) {
            ss >> *maxvalue;
            if (*maxvalue < 0)
              continue;
          }
        }
    
        break;
      }
    
      int count = *width * *height;
    
      switch (type) {
      case PNM_TYPE::P1:
        img->reserve(count);
        read_P1(*img, ifs);
        break;
      case PNM_TYPE::P2:
        img->reserve(count);
        read_P23(*img, ifs);
        break;
      case PNM_TYPE::P3:
        img->reserve(count * 3);
        read_P23(*img, ifs);
        break;
      case PNM_TYPE::P4:
        read_P4(*img, ifs, count);
        break;
      case PNM_TYPE::P5:
        read_P56(*img, ifs);
        break;
      case PNM_TYPE::P6:
        read_P56(*img, ifs);
        break;
      }
    
    }
    

    テスト用コード

    #include <iostream>
    
    #include "pnm_reader.hpp"
    
    
    //! @brief pnmの各型式のデータをppm形式の格納方法に変更する
    //! @param [out] dst 結果画像データ
    //! @param [in] src 元画像データ
    //! @param [in] pixelcount 画素数
    //! @param [in] type P1~P6のいずれか
    //! @return なし
    void pnm_to_ppm(
      std::vector<unsigned char>& dst,
      unsigned char* src,
      int pixelcount,
      PNM_TYPE type
      ) {
    
      switch (type)
      {
      case PNM_TYPE::P1:
      case PNM_TYPE::P4:
      case PNM_TYPE::PBitmap:
        for (size_t i = 0; i < pixelcount; i++) {
          dst.push_back(src[i] ? 0 : 255);
          dst.push_back(src[i] ? 0 : 255);
          dst.push_back(src[i] ? 0 : 255);
        }
        break;
      case PNM_TYPE::P2:
      case PNM_TYPE::P5:
      case PNM_TYPE::PGraymap:
        for (size_t i = 0; i < pixelcount; i++) {
          dst.push_back(src[i]);
          dst.push_back(src[i]);
          dst.push_back(src[i]);
        }
        break;
      case PNM_TYPE::P3:
      case PNM_TYPE::P6:
      case PNM_TYPE::PPixmap:
        for (size_t i = 0; i < pixelcount; i++) {
          dst.push_back(src[i * 3 + 0]);
          dst.push_back(src[i * 3 + 1]);
          dst.push_back(src[i * 3 + 2]);
        }
        break;
      }
    }
    //! @brief PPM(RGB各1byte,カラー,テキスト)を書き込む
    //! @param [in] fname ファイル名
    //! @param [in] vmax 全てのRGBの中の最大値
    //! @param [in] width 画像の幅
    //! @param [in] height 画像の高さ
    //! @param [in] p 画像のメモリへのアドレス
    //! @details RGBRGBRGB....のメモリを渡すと、RGBテキストでファイル名fnameで書き込む
    void pnmP3_Write(
      const char* const fname, 
      const int vmax, 
      const int width, 
      const int height, 
      const unsigned char* const p) {
    
      FILE* fp = fopen(fname, "wb");
      fprintf(fp, "P3\n%d %d\n%d\n", width, height, vmax);
    
      size_t k = 0;
      for (size_t i = 0; i < (size_t)height; i++) {
        for (size_t j = 0; j < (size_t)width; j++) {
          fprintf(fp, "%d %d %d ", p[k * 3 + 0], p[k * 3 + 1], p[k * 3 + 2]);
          k++;
        }
        fprintf(fp, "\n");
      }
    
      fclose(fp);
    }
    int main()
    {
      const char* fpathname = "C:\\test\\butterfly-5546907_640-binary.ppm";
    
    
      std::vector<unsigned char> img;
      int width, height, maxvalue;
      PNM_TYPE type;
      read_pnm(
        fpathname,
        &img,
        &width,
        &height,
        &maxvalue,
        &type
      );
    
      std::vector<unsigned char> tmp;
      pnm_to_ppm(tmp, &img[0], width * height,type);
      //////////////////////////////
      const char* fname = "C:\\test\\out.ppm";
      pnmP3_Write(fname, 255, width, height, &tmp[0]);
    
    
    }
    

    C++ templateで条件を複数並べるif/elifみたいなものを作る

    多分まだ上げてなかったと思う。タイトルはif-elifとしてあるけれど個人的な好みでSwitch / Caseにしてある。

    以下のように、Switch< /* .... */ >::type とし、typeを決定する。

    そのtypeは、Case<条件 , 型 > とし、条件がtrueの時の型とする。

    条件は再起で上から順番に評価していて、trueが見つかった時点で探索をやめるので、複数あると先に書いた方が採用される。

    #include "pch.h"
    #include <iostream>
    
    
    ///////////////////////////////////////////////////////
    // Switch / Case
    

    template
    <bool BOOL, typename Second> struct Case { static constexpr bool TBOOL = BOOL; using TSecond = Second; }; template<class Head, class... Tail> struct Switch { using type = typename std::conditional< Head::TBOOL, typename Head::TSecond, // trueの時 typename Switch<Tail...>::type //falseの時 >::type; }; //再起終端用特殊化 template<class Head> struct Switch<Head> { using type = typename std::conditional< Head::TBOOL, typename Head::TSecond, // trueの時 void >::type; };
    ///////////////////////////////////////////////////////
    // 動作テスト用クラス
    
    
    struct forInt {void disp() {std::cout << "int" << std::endl;}};
    struct forFloat {void disp() {std::cout << "float" << std::endl;}};
    struct forChar {void disp() {std::cout << "char" << std::endl;}};
    //動作テスト用関数
    template<typename T>
    void disp_type(T val) {
    
      // 型 T によってDispTypeが変わる
      using DispType =
        typename Switch<
        Case<std::is_same<int, T>::value, forInt>,
        Case<std::is_same<float, T>::value, forFloat>,
        Case<std::is_same<char, T>::value, forChar>
        >::type;
    
      DispType tmp;
    
      tmp.disp();
    }
    int main()
    {
      char value = 10;
    
      disp_type(value);
    
    }
    

    C++ templateで配列の種類を特定する

    C++には T[N] 形式の他に、

    ・std::array
    ・std::vector
    ・std::valarray

    等の配列の形式がある。このうちstdの<type_traits>で判別できるのはT[N]型か否かを判別するstd::is_arrayしか用意されていないようなので、その他を判別するテンプレートを用意する。

    #include <iostream>
    #include<vector>
    #include<array>
    #include<valarray>
    
    int primarray[10];
    std::array<int, 10> stdarray;
    std::vector<int> vectordata;
    std::valarray<int> valarraydata;
    
    namespace CheckPrimitiveArray {
      // type_traits内に同様のものがある
    
    
      template<class>
      struct is_array :std::false_type {};
    
      template<class T>
      struct is_array<T[]> : std::true_type {};
    
      template<class T, std::size_t N>
      struct is_array<T[N]> : std::true_type {};
      void example() {
        std::cout << std::endl;
        std::cout << "--- is_array" << std::endl;
        std::cout << "primitive:" << is_array<decltype(primarray)>::value << std::endl;
        std::cout << "stdarray :" << is_array<decltype(stdarray)>::value << std::endl;
        std::cout << "vector   :" << is_array<decltype(vectordata)>::value << std::endl;
        std::cout << "valarray :" << is_array<decltype(valarraydata)>::value << std::endl;
      }
    
    }
    
    namespace CheckVectorArray {
    
    
      template<class>
      struct is_vector : std::false_type {};
    
    template<class T, class ALLOCATOR> struct is_vector<std::vector<T, ALLOCATOR>> : std::true_type {};
      void example() {
        std::cout << std::endl;
        std::cout << "--- is_vector" << std::endl;
        std::cout << "primitive:" << is_vector<decltype(primarray)>::value << std::endl;
        std::cout << "stdarray :" << is_vector<decltype(stdarray)>::value << std::endl;
        std::cout << "vector   :" << is_vector<decltype(vectordata)>::value << std::endl;
        std::cout << "valarray :" << is_vector<decltype(valarraydata)>::value << std::endl;
      }
    
    }
    namespace CheckStdArray {
    
    
      template<class>
      struct is_stdarray :std::false_type {};
    
      template<class T, std::size_t N>
      struct is_stdarray<std::array<T, N>> :std::true_type {};
      void example() {
        std::cout << std::endl;
        std::cout << "--- is_stdarray" << std::endl;
        std::cout << "primitive:" << is_stdarray<decltype(primarray)>::value << std::endl;
        std::cout << "stdarray :" << is_stdarray<decltype(stdarray)>::value << std::endl;
        std::cout << "vector   :" << is_stdarray<decltype(vectordata)>::value << std::endl;
        std::cout << "valarray :" << is_stdarray<decltype(valarraydata)>::value << std::endl;
      }
    }
    namespace CheckValArray {
    
    
      template<class>
      struct is_valarray : std::false_type {};
    
    template<class T> struct is_valarray<std::valarray<T>> : std::true_type {};
      void example() {
        std::cout << std::endl;
        std::cout << "--- is_valarray" << std::endl;
        std::cout << "primitive:" << is_valarray<decltype(primarray)>::value << std::endl;
        std::cout << "stdarray :" << is_valarray<decltype(stdarray)>::value << std::endl;
        std::cout << "vector   :" << is_valarray<decltype(vectordata)>::value << std::endl;
        std::cout << "valarray :" << is_valarray<decltype(valarraydata)>::value << std::endl;
      }
    }
    
    
    int main()
    {
      CheckPrimitiveArray::example();
      CheckStdArray::example();
      CheckVectorArray::example();
      CheckValArray::example();
    
      getchar();
    }
    

    判別結果

    --- is_array
    primitive:1
    stdarray :0
    vector   :0
    valarray :0
    
    --- is_stdarray
    primitive:0
    stdarray :1
    vector   :0
    valarray :0
    
    --- is_vector
    primitive:0
    stdarray :0
    vector   :1
    valarray :0
    
    --- is_valarray
    primitive:0
    stdarray :0
    vector   :0
    valarray :1

    Blender Snowのチュートリアルを試す

    以下のようにノードを設定する

    結果

    応用

    以下のようにCtrl+Gでノードグループとしてひとまとめにし、NormalのdotをFacにしてオブジェクトの本来のマテリアルとミックスすれば、オブジェクトの色と雪の色を分離できる。

    Blender Cycles White Noise Texture

    いつの間にかCyclesにWhite Noise Textureが入っていた。確認しているバージョンは2.83,2.90。確か2.7系には無かった気がする。Blender Renderが廃止された?時に移行があったのかもしれない。

    基本的なノード接続

    応用

    そのままだとあまりにもわかりにくいので、vector mathでsnapを設定する。

    Blender のSky Textureを半球にする

    SkyTextureは便利だが水平面より下にも色がついている。これを編集し水平面には色がつかないようにする。

    Worldで以下のようにノードを設定する。

    そしてSkyTextureとMixRGBでMultiplyする

    応用で、空の部分だけを星空にする。

    Make a Procedural Starry Sky HDRI with Nodes | Blender 2.82 のチュートリアルを試す

    Make a Procedural Starry Sky HDRI with Nodes | Blender 2.82

    星空のチュートリアルで比較的短い物。

    ざっくりいうとBackgroundを以下のノードで設定する。

    ⑥のFrom Minを0に近づけると明るくなる。

    EEVEEの場合レンダリング結果に直接Bloomがかかるのでわかりやすいが、Cyclesの場合は後からcompositorで設定しなければならない。

    CyclesでのBloomの設定

    compositorで設定する。

    色々と加工してみた例。