ぬの部屋(仮)
nu-no-he-ya
  • 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
           
  • 実数のピクセル座標を確認するための出力クラスを作った

    例えば ( -0.1 , 0.1 ) というピクセル座標は書き込まれる際には(0,0)というピクセルになってしまうので、小数点以下の計算結果を目視で確認できない。これをできるクラスを作った。

    CRealPixel.hpp

    #pragma once
    
    #include <vector>
    #include <array>
    
    struct rgb_t {
      std::array<unsigned char, 3> data;
    
    };
    
    // 実数座標と色情報
    struct dpixel_t {
      double x, y;
      rgb_t c;
      dpixel_t(double x_, double y_, rgb_t c_) :
        x(x_), y(y_), c(c_) {}
    };
    
    // 一つのピクセル。
    // そのピクセルの色と、そのピクセル内に入った実数座標の点が記録できる
    struct pixel_t {
      rgb_t color;
      std::vector< dpixel_t > points;
    public:
      bool plot(const double x, const double y,const rgb_t rgba) {
        if (abs(x) > 0.5)
          return false;
    
        points.emplace_back(x,y, rgba);
      }
      void set(rgb_t c) {
        color = c;
      }
      size_t count()const { return points.size(); }
    
    };
    
    // 実数型のピクセルを書き込める画像データ型
    class CRealPixel {
    
      int _width;
      int _height;
    
      std::vector< pixel_t > _img;
    
    public:
    
      // 画像のピクセル数(整数)を指定
      void set_size(int width_,int height_) {
        _width = width_;
        _height = height_;
        _img.resize(_width * _height);
    
      }
    
      // 画像のピクセル数を取得
      size_t size()const { return _width * _height; }
    
      //画素へ書き込み(整数のIndex)
      rgb_t& operator[](const size_t index) {
        return _img[index].color;
    
      }
    
      // 画素を取得(二次元)
      rgb_t& get(const int x, const int y) {
        return operator[](y * _width + x);
      }
    
      // 実数座標の頂点を追加
      void plot(const double x_, const double y_, rgb_t c_) {
        int ix = round(x_);
        int iy = round(y_);
        double x = x_ - ix;
        double y = y_ - iy;
        _img[iy * _width + ix].plot(x, y, c_);
      }
    
      // RGBの画像を作成
      // まず画素の色で塗りつぶし、その中に実数座標の頂点をプロットする
      // img_ ... 結果の出力先。RGBの配列。
      // ratio_ ... 1ピクセルを何ピクセルで表現するか
      // width ... img_の画素数。 width = _width * ratio_;
      // height ... img_の画素数。 height = _height * ratio_;
      void make_int(std::vector<rgb_t>& img_,int ratio_,int*width,int*height) {
    
        int rwidth = _width * ratio_;
        int rheight = _height * ratio_;
        *width = rwidth;
        *height = rheight;
    
        img_.resize(rwidth * rheight);
    
        for (size_t x = 0; x < _width; x++) {
          for (size_t y = 0; y < _height; y++) {
    
            // このブロックを全着色
            for (size_t ax = x*ratio_; ax < (x+1)*ratio_; ax++) {
              for (size_t ay = y * ratio_; ay < (y + 1) * ratio_; ay++) {
    
                img_[ay * rwidth + ax] = _img[y * _width + x].color;
    
    
              }
            }
    
            // このブロック内の小数点以下のピクセルをプロット
            for (size_t j = 0; j < _img[y * _width + x].count(); j++) {
              double dx = _img[y * _width + x].points[j].x;
              double dy = _img[y * _width + x].points[j].y;
    
              //このブロックの中央
              dx += 0.5;
              dy += 0.5;
    
              int rx = x * ratio_ + (ratio_ * dx);
              int ry = y * ratio_ + (ratio_ * dy);
    
              img_[ry * rwidth + rx] = _img[y * _width + x].points[j].c;
    
            }
          }
    
        }
    
      }
    };
    

    使い方

    #include <iostream>
    
    #pragma warning(disable:4996)
    
    #include "CRealPixel.hpp"
    
    ////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////
    
    void Bresenham(
      CRealPixel& img,
      const int width,
      const int height,
      const int sx,
      const int sy,
      const int ex,
      const int ey,
      const rgb_t color
    );
    
    ////////////////////////////////////////////////////////
    
    void ppmP3_write(
      const char* const fname,
      const int width,
      const int height,
      const unsigned char* const p,
      const int vmax
    );
    
    
    int main()
    {
      int width = 100;
      int height = 40;
    
    
      CRealPixel crp;
    
      crp.set_size(width, height);
    
      // クリア
      for (size_t i = 0; i < crp.size(); i++) {
        crp[i] = rgb_t{ 255,255,255 };
      }
    
      // ブレゼンハムで書き込み
      int sx = 5;
      int sy = 7;
      int ex = 90;
      int ey = 30;
      Bresenham(crp, width, height, sx, sy, ex, ey, { 0,0,0 });
    
      // 浮動小数点型で書き込み
      double dx = ex - sx;
      double dy = ey - sy;
      double a = dy / dx;
      double b = 7 - a * 5;
      for (float fx = sx; fx < ex; fx+=0.1) {
    
        float fy = a * fx + b;
    
        if (round(fx) >= width)break;
        if (round(fy) >= height)break;
    
        crp.plot(fx,fy, { 255,0,0 });
    
      }
    
      // ピクセル単位で書き込まれた内容と、実数単位で書き込まれた内容を
      // RGBの画像へ変換
      std::vector<rgb_t> ii;
      int w, h;
      crp.make_int(ii, 5, &w, &h);
    
      // 変換した画像をファイル保存
      ppmP3_write(
        "aaa.ppm",
        w, h,
        &ii[0].data[0], 255
      );
    
    }
    
    
    //////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////
    
    
    //! @brief ブレゼンハムの直線描画
    //! @param [out] img 画像データ(一次元配列)へのポインタ
    //! @param [in] width 画像の幅(画素数)
    //! @param [in] height 画像の高さ(画素数)
    //! @param [in] sx 線の始点X
    //! @param [in] sy 線の始点Y
    //! @param [in] ex 線の終点X
    //! @param [in] ey 線の終点Y
    //! @param [in] color 線の色
    void Bresenham(
      CRealPixel& img,
      const int width,
      const int height,
      const int sx,
      const int sy,
      const int ex,
      const int ey,
      const rgb_t color
    ) {
    
      const int dx = std::abs(ex - sx);
      const int dy = std::abs(ey - sy);
    
      const int nx = (sx < ex) ? 1 : -1;
      const int ny = (sy < ey) ? 1 : -1;
    
      int err = dx - dy;
    
      int x = sx;
      int y = sy;
      while (1) {
    
        if (x >= 0 && y >= 0 && x < width && y < height) {
          img[y * width + x] = color;
        }
    
        if (x == ex && y == ey)
          break;
    
        const int e2 = 2 * err;
        if (e2 > -dy) {
          err = err - dy;
          x += nx;
        }
        if (e2 < dx) {
          err = err + dx;
          y += ny;
        }
      }
    }
    
    
    
    // 
    // 
    // 
    /////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////
    
    //! @brief PPM(RGB各1byte,カラー,テキスト)を書き込む
    //! @param [in] fname ファイル名
    //! @param [in] width 画像の幅
    //! @param [in] height 画像の高さ
    //! @param [in] p 画像のメモリへのアドレス
    //! @param [in] vmax 全てのRGBの中の最大値。普通の画像なら255
    //! @details RGBRGBRGB....のメモリを渡すと、RGBテキストでファイル名fnameで書き込む
    void ppmP3_write(
      const char* const fname,
      const int width,
      const int height,
      const unsigned char* const p,
      const int vmax
    ) {
    
      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);
    }
    

    結果

    結果の拡大

    Bezier曲線をなめらかにする(やや強引)

    tを適当にstepを決めて進めてその頂点をブレゼンハムなどで結ぶと折れ線になってしまうので、なめらかな曲線にできないか検討した。

    Bezierのtを求める

    Bezierの式はこうなっている。

    これを展開してまとめなおしてみる。

    すると普通の二次方程式にできる。P1,P2,P3は定数なので、pが分かれば解の公式でtを求められる。

    仮にある点pのピクセルが点灯したとして、その次の点は、pの周辺の8点のどれかであるから、例えばp.x-1 , p.x , p.x+1 を使って 解の公式を使えば、p.xがその値の時のtが得られる。

    本当は連立方程式なりなんなりを解けばいいのかもしれないが、私にそんな知恵はないので、p.x-1,p.x,p.x+1 , p.y-1,p.y,p.y+1 のそれぞれに対応するtを求め、一番直前のピクセルに近い距離のものを選ぶ。

    ただし二次方程式なので、解が虚数になることがある。この場合は対象外。さらに解が二つ出てきたら、どちらかが「現在のtの値」より小さい、つまりバックしてしまう点なので、そうならないほうを選ぶ。

    ソースコード

    #pragma warning(disable:4996)
    
    #include <iostream>
    
    #include <array>
    #include <vector>
    #include <optional>
    
    using rgb_t = std::array<unsigned char, 3>;
    
    struct Image_t {
      int width;
      int height;
      std::vector<rgb_t> data;
      Image_t(int _width_, int _height_) {
        width = _width_;
        height = _height_;
        data = std::vector<rgb_t>(width * height);
      }
    
      inline int pos(const int x, const int y)const {
        return y * width + x;
      }
      inline bool valid(const int x, const int y)const {
        if (x < 0)return false;
        if (y < 0)return false;
        if (x >= width)return false;
        if (y >= height)return false;
      }
    };
    
    void ppmP3_write(
      const char* const fname,
      const int width,
      const int height,
      const unsigned char* const p
    );
    
    
    struct coord_t { 
      int x, y; 
      bool operator==(const coord_t& src) {
        return x == src.x && y == src.y;
      }
      bool operator!=(const coord_t& src) {
        return x != src.x || y != src.y;
      }
    };
    struct coordd_t {
      double x, y;
    };
    
    
    
    //! @brief 画像をクリア
    //! @param [in,out] img クリアする画像
    //! @param [in] color クリアする色
    //! @return なし
    void fill(Image_t& img, const rgb_t color) {
      std::fill(img.data.begin(), img.data.end(), color);
    }
    
    //! @brief 大きさのある点(矩形)を書き込む
    //! @param [in,out] img 書き込む画像
    //! @param [in] pos 書き込む位置
    //! @param [in] color 書き込む色
    //! @param [in] size 点の大きさ
    //! @return なし
    void plot(Image_t& img, const coord_t pos, const rgb_t color, const int size) {
    
      int bx = pos.x - size / 2;
      int ax = bx + size;
      int by = pos.y - size / 2;
      int ay = by + size;
      for (size_t x = bx; x < ax; x++) {
        for (size_t y = by; y < ay; y++) {
          if (img.valid(x, y)) {
            img.data[img.pos(x, y)] = color;
          }
        }
      }
    
    }
    
    struct QuadraticEquation {
      double plus;
      double minus;
    };
     
    
          
    //! @brief Bezierのtを求める関数
    //! @param [in] P1 最初の点の座標
    //! @param [in] P2 ハンドルの点の座標
    //! @param [in] P3 最後の点の座標
    //! @param [in] p求めたい点の座標
    //! @return pを得るためのtの値 値は零個~最大二個。零個の時はstd::nulloptを返す
    std::optional<QuadraticEquation> BezierInverse(double P1,double P2,double P3,double p) {
      double a = P1 - 2 * P2 + P3;
      double b = 2 * (-P1 + P2);
      double c = P1 - p;
    
      double test = pow(b, 2) - 4 * a * c;
    
      // (-b +- sqrt(test)) /  (2*a)
    
      double plus,minus;
      if (test < 0) {
        //sqrtの中が負数なら虚数なので解をなしとする
        return std::nullopt;
      }
      else {
        plus = (-b + sqrt(test)) / (2 * a);
        minus = (-b - sqrt(test)) / (2 * a);
      }
    
      
      return QuadraticEquation{ plus,minus };
    }
          
          
    coordd_t BezierXY(coord_t P1, coord_t P2, coord_t P3, double t) {
    
      double ti = 1.0 - t;
    
      coordd_t dP;
      dP.x = 
        pow(ti, 2) * P1.x +
        2 * ti * t * P2.x +
        pow(t, 2) * P3.x;
      dP.y = 
        pow(ti, 2) * P1.y +
        2 * ti * t * P2.y +
        pow(t, 2) * P3.y;
    
      return dP;
    
    }
          
                
    //! @brief Bézier Curve
    //! @param [in,out] img 書き込む画像
    //! @param [in] 制御点三点
    //! @param [in] color 書き込む色
    //! @return なし
    //! @sa https://ja.javascript.info/bezier-curve
    void Bezier(Image_t& img, const std::array<coord_t, 3>& points, const rgb_t& color) {
    
      const int step = 30;
    
    
      coord_t P1 = points[0];
      coord_t P2 = points[1];
      coord_t P3 = points[2];
    
      coord_t Pbefore = P1;
    
      //最初の一点をプロット
      if (img.valid(P1.x, P1.y)) {
        plot(img, P1, color, 1);
      }
    
      // tの候補を確認
      std::vector< double > cands;
    
      double t = 0.0;
      while (t < 1.0 && Pbefore!=P3){
    
        coord_t P = Pbefore;
    
        double derror = (std::numeric_limits<double>::max)();
        double rett = 100;
        coord_t Pn;
        coord_t tmp;
        coordd_t cd;
    
        cands.clear();
    
        // 現在の頂点の周辺
        for (int x = -1; x <= 1; x++) {
          auto tx = BezierInverse(P1.x, P2.x, P3.x, P.x + x);
          if (tx != std::nullopt) {
            cands.push_back(tx.value().plus);
            cands.push_back(tx.value().minus);
          }
        }
        for (int y = -1; y <= 1; y++) {
          auto ty = BezierInverse(P1.y, P2.y, P3.y, P.y + y);
          if (ty != std::nullopt) {
            cands.push_back(ty.value().plus);
            cands.push_back(ty.value().minus);
          }
        }
    
        for (auto tt : cands) {
    
          // 直前の点を描いたtより大きく、
          // かつ0.0~1.0に収まっている場合
          if (tt > t && tt > 0.0 && tt <= 1.0 ) {
    
            cd = BezierXY(P1, P2, P3, tt);
            tmp.x = round(cd.x);
            tmp.y = round(cd.y);
    
            if (tmp != P) {
              
              double d = pow(cd.x - P.x, 2) + pow(cd.y - P.y, 2);
              if (d < derror) {
    
                Pn = tmp;
    
                derror = d;
    
                rett = tt;
              }
            }
    
          }
    
        }
    
        t = rett;
        if (img.valid(Pn.x, Pn.y)) {
          plot(img, Pn, color, 1);
        }
        else {
          printf("%d %d", Pn.x, Pn.y);
        }
    
        Pbefore = Pn;
    
      }
    
    }

    int
    main() { Image_t img(400, 250); fill(img, rgb_t{ 255,255,255 }); std::array<coord_t, 3> vcd; vcd[2] = { 50,20 }; vcd[1] = { 200,200 }; vcd[0] = { 300,60 }; plot(img, vcd[0], rgb_t{ 125,0,0 }, 5);// ハンドルを描画 plot(img, vcd[1], rgb_t{ 125,0,0 }, 5); plot(img, vcd[2], rgb_t{ 125,0,0 }, 5); Bezier(img, vcd, rgb_t{ 0,0,255 });// ベジェ曲線を描く ppmP3_write("abc.ppm", img.width, img.height, (unsigned char*)img.data.data()); } //! @brief PPM(RGB各1byte,カラー,テキスト)を書き込む //! @param [in] fname ファイル名 //! @param [in] width 画像の幅 //! @param [in] height 画像の高さ //! @param [in] p 画像のメモリへのアドレス //! @param [in] vmax 全てのRGBの中の最大値。普通の画像なら255 //! @details RGBRGBRGB....のメモリを渡すと、RGBテキストでファイル名fnameで書き込む void ppmP3_write( const char* const fname, 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, 255); 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); }

    実行例

    Bezier曲線を描いてみる

    コード

    #pragma warning(disable:4996)
    
    #include <iostream>
    
    #include "Bresenham.hpp"
    
    #include <array>
    #include <vector>
    
    using rgb_t = std::array<unsigned char, 3>;
    
    struct Image_t {
      int width;
      int height;
      std::vector<rgb_t> data;
      Image_t(int _width_, int _height_) {
        width = _width_;
        height = _height_;
        data = std::vector<rgb_t>(width * height);
      }
    
      inline int pos(const int x, const int y)const {
        return y * width + x;
      }
      inline bool valid(const int x, const int y)const {
        if (x < 0)return false;
        if (y < 0)return false;
        if (x >= width)return false;
        if (y >= height)return false;
      }
    };
    
    void ppmP3_write(
      const char* const fname,
      const int width,
      const int height,
      const unsigned char* const p
    );
    
    
    struct coord_t { int x, y; };
    
    
    void Bresenham(
      Image_t& img,
      const coord_t s,
      const coord_t e,
      const rgb_t color
    ) {
      Bresenham(img.data.data(), img.width, img.height, s.x, s.y, e.x, e.y, color);
    }
    
    //! @brief 画像をクリア
    //! @param [in,out] img クリアする画像
    //! @param [in] color クリアする色
    //! @return なし
    void fill(Image_t& img, const rgb_t color) {
      std::fill(img.data.begin(), img.data.end(), color);
    }
    
    
    
          
    //! @brief 大きさのある点(矩形)を書き込む
    //! @param [in,out] img 書き込む画像
    //! @param [in] pos 書き込む位置
    //! @param [in] color 書き込む色
    //! @param [in] size 点の大きさ
    //! @return なし
    void plot(Image_t& img, const coord_t pos, const rgb_t color, const int size) {
    
      int bx = pos.x - size / 2;
      int ax = bx + size;
      int by = pos.y - size / 2;
      int ay = by + size;
      for (size_t x = bx; x < ax; x++) {
        for (size_t y = by; y < ay; y++) {
          if (img.valid(x, y)) {
            img.data[img.pos(x, y)] = color;
          }
        }
      }
    
    }
    

    //! @brief Bézier Curve
    //! @param [in,out] img 書き込む画像
    //! @param [in] 制御点三点
    //! @param [in] color 書き込む色
    //! @return なし
    //! @sa https://ja.javascript.info/bezier-curve
    void Bezier(Image_t& img,const std::array<coord_t,3>& points, const rgb_t& color) {
    
      const int step=30;
    
    
      coord_t P1 = points[0];
      coord_t P2 = points[1];
      coord_t P3 = points[2];
    
      for (size_t s = 0; s < step; s++) {
        double t = s / (double)step;
        double ti = 1.0 - t;
        coord_t P;
    
        P.x =
          pow(ti, 2) * P1.x +
          2 * ti * t * P2.x +
          pow(t, 2) * P3.x;
        P.y =
          pow(ti, 2) * P1.y +
          2 * ti * t * P2.y +
          pow(t, 2) * P3.y;
    
        if (img.valid(P.x, P.y)) {
    
          plot(img, P, color, 3);
    
        }
      }
    
    }
    
          

    int
    main() { Image_t img(400,300); fill(img, rgb_t{ 255,255,255 }); std::array<coord_t,3> vcd; vcd[0] = { 50,20 }; vcd[1] = {200,200}; vcd[2] = { 300,60 }; Bezier(img, vcd, rgb_t{ 0,0,255 });// ベジェ曲線を描く Bresenham(img, vcd[0], vcd[1], rgb_t{ 0,0,0 });// ブレゼンハムでハンドルを結ぶ線を描く Bresenham(img, vcd[2], vcd[1], rgb_t{ 0,0,0 }); plot(img, vcd[0], rgb_t{ 125,0,0 }, 5);// ハンドルを描画 plot(img, vcd[1], rgb_t{ 125,0,0 }, 5); plot(img, vcd[2], rgb_t{ 125,0,0 }, 5); ppmP3_write("abc.ppm", img.width, img.height, (unsigned char*)img.data.data()); } //! @brief PPM(RGB各1byte,カラー,テキスト)を書き込む //! @param [in] fname ファイル名 //! @param [in] width 画像の幅 //! @param [in] height 画像の高さ //! @param [in] p 画像のメモリへのアドレス //! @param [in] vmax 全てのRGBの中の最大値。普通の画像なら255 //! @details RGBRGBRGB....のメモリを渡すと、RGBテキストでファイル名fnameで書き込む void ppmP3_write( const char* const fname, 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, 255); 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); }

    なお、ハンドルを結ぶ直線を描画するブレゼンハムは作り置きのものを使う。

    実行例

    FreeType2でフォントファイルのSFNT Nameを読んでフォント名などを取り出す(2)

    SFNT Nameを調べるために作ったテーブルをせっかくなのでおいておく。

    main.cpp

    #pragma warning(disable:4996)
    
    #include <iostream>
    
    #include <ft2build.h>
    #include FT_FREETYPE_H
    
    #pragma comment(lib,"freetype.lib")
    
    
    #include "disp-sfnt-name.hpp"
    
    
    //////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////
    // 各テーブル
    
    std::unordered_map<unsigned int, NameID> nameIDTable;
    std::unordered_map<unsigned int, LanguageID> languageIDTable;
    std::unordered_map<unsigned int, PlatformID> platformIDTable;
    
    std::unordered_map<unsigned int, EncodingID_Apple> encodingIDAppleTable;
    std::unordered_map<unsigned int, EncodingID_MACINTOSH>  encodingIDMacTable;
    std::unordered_map<unsigned int, EncodingID_ISO>  encodingIDISOTable;
    std::unordered_map<unsigned int, EncodingID_Microsoft>  encodingIDMicrosoftTable;
    std::unordered_map<unsigned int, EncodingID_Adobe>  encodingIDAdobeTable;
    //////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////
    // 表示用関数
    
    void disp_sfnt(const FT_SfntName& sname);
    
    //////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////
    
    int main()
    {
        setlocale(LC_ALL, "");
    
        //テーブルを作成
        create_language_id_list(languageIDTable);
        create_name_id_list(nameIDTable);
        create_platform_id_list(platformIDTable);
    
        create_encoding_id_apple(encodingIDAppleTable);
        create_encoding_id_MAC(encodingIDMacTable);
        create_encoding_id_iso(encodingIDISOTable);
        create_encoding_id_Microsoft(encodingIDMicrosoftTable);
        create_encoding_id_Adobe(encodingIDAdobeTable);
    
    
    
        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\\ALGER.ttf",
            //"C:\\Windows\\Fonts\\meiryo.ttc",
            "C:\\Windows\\Fonts\\msmincho.ttc",
            0,
            &face
        );
    
        FT_Int count = FT_Get_Sfnt_Name_Count(face);
    
        FT_SfntName sfnt_name;
    
        for (FT_Int i = 0; i < count; i++) {
    
            FT_Get_Sfnt_Name(face, i, &sfnt_name);
    
    
            switch (sfnt_name.platform_id) {
            case  TT_PLATFORM_APPLE_UNICODE:
                if (0) {
                    printf("%s / ", platformIDTable[sfnt_name.platform_id].macro.c_str()); ;
                    printf("%s / ", encodingIDAppleTable[sfnt_name.encoding_id].macro.c_str());
                    disp_sfnt(sfnt_name);
                }
                break;
            case  TT_PLATFORM_MACINTOSH:
                if (0) {
                    printf("%s / ", platformIDTable[sfnt_name.platform_id].macro.c_str()); ;
                    printf("%s / ", encodingIDMacTable[sfnt_name.encoding_id].macro.c_str());
                    disp_sfnt(sfnt_name);
                }
                break;
            case  TT_PLATFORM_ISO:
                if (0) {
                    printf("%s / ", platformIDTable[sfnt_name.platform_id].macro.c_str()); ;
                    printf("%s / ", encodingIDISOTable[sfnt_name.encoding_id].macro.c_str());
                    disp_sfnt(sfnt_name);
                }
                break;
            case  TT_PLATFORM_MICROSOFT:
                if (1) {
                    printf("%s / ", platformIDTable[sfnt_name.platform_id].macro.c_str()); ;
                    printf("%s / ", encodingIDMicrosoftTable[sfnt_name.encoding_id].macro.c_str());
                    disp_sfnt(sfnt_name);
                }
                break;
            case  TT_PLATFORM_CUSTOM:
                break;
            case  TT_PLATFORM_ADOBE:
                if (0) {
                    printf("%s / ", platformIDTable[sfnt_name.platform_id].macro.c_str()); ;
                    printf("%s / ", encodingIDAdobeTable[sfnt_name.encoding_id].macro.c_str());
                    disp_sfnt(sfnt_name);
                }
                break;
            }
    
        }
    
        // FreeType2の解放
        FT_Done_Face(face);
        FT_Done_FreeType(library);
    }
    
    void disp_sfnt(
        const FT_SfntName& sname) {
    
       
        printf("%s / ", nameIDTable[sname.name_id].macro.c_str());
        printf("%s / ", languageIDTable[sname.language_id].Primary_Language.c_str());
    
        std::string astr;
        std::wstring wstr;
    
        printf("::name==");
        switch (sname.encoding_id) {
        case 0:
            for (size_t i = 0; i < sname.string_len; i++) {
                astr += sname.string[i];
            }
            printf("%s", astr.c_str());
            break;
        default:
    
            for (size_t i = 0; i < sname.string_len; i+=2) {
                char16_t w = *((char16_t*)&sname.string[i]);
                unsigned char* p = (unsigned char*)&w;
                // UTF16BEなので2バイトを反転する
                std::swap(p[0], p[1]);
    
                wstr += w;
            }
            printf("%ls", wstr.c_str());
        }
        puts("");
    
    
    
    }
    

    disp-sfnt-name.hpp

    #pragma once
    
    #include <unordered_map>
    
    
    //FT_Get_Sfnt_Name_Count
    #include<freetype/ftsnames.h>
    
    // プラットフォーム TT_PLATFORM_XXX の定義
    #include<freetype/ttnameid.h>
    
    struct LanguageID {
        unsigned int LCID;
        std::string macro;
        std::string Primary_Language;
        std::string Region;
    };
    struct NameID {
        int ID;
        std::string macro;
    };
    struct PlatformID {
        int ID;
        std::string macro;
    };
    
    struct EncodingID_Apple {
        int ID;
        std::string macro;
    };
    
    struct EncodingID_MACINTOSH {
        int ID;
        std::string macro;
    };
    struct EncodingID_ISO {
        int ID;
        std::string macro;
    };
    struct EncodingID_Microsoft {
        int ID;
        std::string macro;
    };
    struct EncodingID_Adobe {
        int ID;
        std::string macro;
    };
    
    
    void create_platform_id_list(std::unordered_map<unsigned int, PlatformID>& m) {
        m[TT_PLATFORM_APPLE_UNICODE] = PlatformID{ TT_PLATFORM_APPLE_UNICODE,"TT_PLATFORM_APPLE_UNICODE" };
        m[TT_PLATFORM_MACINTOSH] = PlatformID{ TT_PLATFORM_MACINTOSH    ,"TT_PLATFORM_MACINTOSH" };
        m[TT_PLATFORM_ISO] = PlatformID{ TT_PLATFORM_ISO          ,"TT_PLATFORM_ISO" };
        m[TT_PLATFORM_MICROSOFT] = PlatformID{ TT_PLATFORM_MICROSOFT    ,"TT_PLATFORM_MICROSOFT" };
        m[TT_PLATFORM_CUSTOM] = PlatformID{ TT_PLATFORM_CUSTOM       ,"TT_PLATFORM_CUSTOM" };
        m[TT_PLATFORM_ADOBE] = PlatformID{ TT_PLATFORM_ADOBE        ,"TT_PLATFORM_ADOBE" };
    }
    
    void create_language_id_list(std::unordered_map<unsigned int, LanguageID>& m) {
    
        m[TT_MS_LANGID_ENGLISH_PHILIPPINES] = LanguageID{ TT_MS_LANGID_ENGLISH_PHILIPPINES, "TT_MS_LANGID_ENGLISH_PHILIPPINES","English","Republic of the Philippines" };
        m[TT_MS_LANGID_ARABIC_TUNISIA] = LanguageID{ TT_MS_LANGID_ARABIC_TUNISIA, "TT_MS_LANGID_ARABIC_TUNISIA","Arabic","Tunisia" };
        m[TT_MS_LANGID_ARABIC_ALGERIA] = LanguageID{ TT_MS_LANGID_ARABIC_ALGERIA, "TT_MS_LANGID_ARABIC_ALGERIA","Arabic","Algeria" };
        m[TT_MS_LANGID_ENGLISH_CARIBBEAN] = LanguageID{ TT_MS_LANGID_ENGLISH_CARIBBEAN, "TT_MS_LANGID_ENGLISH_CARIBBEAN","English","Caribbean" };
        m[TT_MS_LANGID_SWEDISH_FINLAND] = LanguageID{ TT_MS_LANGID_SWEDISH_FINLAND, "TT_MS_LANGID_SWEDISH_FINLAND","Sweden","Finland" };
        m[TT_MS_LANGID_ARABIC_EGYPT] = LanguageID{ TT_MS_LANGID_ARABIC_EGYPT, "TT_MS_LANGID_ARABIC_EGYPT","Arabic","Egypt" };
        m[TT_MS_LANGID_ENGLISH_TRINIDAD] = LanguageID{ TT_MS_LANGID_ENGLISH_TRINIDAD, "TT_MS_LANGID_ENGLISH_TRINIDAD","English","Trinidad and Tobago" };
        m[TT_MS_LANGID_BENGALI_BANGLADESH] = LanguageID{ TT_MS_LANGID_BENGALI_BANGLADESH, "TT_MS_LANGID_BENGALI_BANGLADESH","Bengali","Bangladesh" };
        m[TT_MS_LANGID_ARABIC_SAUDI_ARABIA] = LanguageID{ TT_MS_LANGID_ARABIC_SAUDI_ARABIA, "TT_MS_LANGID_ARABIC_SAUDI_ARABIA","Arabic","Saudi Arabia" };
        m[TT_MS_LANGID_ENGLISH_SINGAPORE] = LanguageID{ TT_MS_LANGID_ENGLISH_SINGAPORE, "TT_MS_LANGID_ENGLISH_SINGAPORE","English","Singapore" };
        m[TT_MS_LANGID_ENGLISH_UNITED_KINGDOM] = LanguageID{ TT_MS_LANGID_ENGLISH_UNITED_KINGDOM, "TT_MS_LANGID_ENGLISH_UNITED_KINGDOM","English","United Kingdom" };
        m[TT_MS_LANGID_ARABIC_OMAN] = LanguageID{ TT_MS_LANGID_ARABIC_OMAN, "TT_MS_LANGID_ARABIC_OMAN","Arabic","Oman" };
        m[TT_MS_LANGID_ENGLISH_ZIMBABWE] = LanguageID{ TT_MS_LANGID_ENGLISH_ZIMBABWE, "TT_MS_LANGID_ENGLISH_ZIMBABWE","English","Zimbabwe" };
        m[TT_MS_LANGID_ISIZULU_SOUTH_AFRICA] = LanguageID{ TT_MS_LANGID_ISIZULU_SOUTH_AFRICA, "TT_MS_LANGID_ISIZULU_SOUTH_AFRICA","isiZulu","South Africa" };
        m[TT_MS_LANGID_ARABIC_MOROCCO] = LanguageID{ TT_MS_LANGID_ARABIC_MOROCCO, "TT_MS_LANGID_ARABIC_MOROCCO","Arabic","Morocco" };
        m[TT_MS_LANGID_ARABIC_LIBYA] = LanguageID{ TT_MS_LANGID_ARABIC_LIBYA, "TT_MS_LANGID_ARABIC_LIBYA","Arabic","Libya" };
        m[TT_MS_LANGID_ENGLISH_JAMAICA] = LanguageID{ TT_MS_LANGID_ENGLISH_JAMAICA, "TT_MS_LANGID_ENGLISH_JAMAICA","English","Jamaica" };
        m[TT_MS_LANGID_CZECH_CZECH_REPUBLIC] = LanguageID{ TT_MS_LANGID_CZECH_CZECH_REPUBLIC, "TT_MS_LANGID_CZECH_CZECH_REPUBLIC","Czech","Czech Republic" };
        m[TT_MS_LANGID_BENGALI_INDIA] = LanguageID{ TT_MS_LANGID_BENGALI_INDIA, "TT_MS_LANGID_BENGALI_INDIA","Bengali","India" };
        m[TT_MS_LANGID_ARABIC_IRAQ] = LanguageID{ TT_MS_LANGID_ARABIC_IRAQ, "TT_MS_LANGID_ARABIC_IRAQ","Arabic","Iraq" };
        m[TT_MS_LANGID_INDONESIAN_INDONESIA] = LanguageID{ TT_MS_LANGID_INDONESIAN_INDONESIA, "TT_MS_LANGID_INDONESIAN_INDONESIA","Indonesian","Indonesia" };
        m[TT_MS_LANGID_ENGLISH_AUSTRALIA] = LanguageID{ TT_MS_LANGID_ENGLISH_AUSTRALIA, "TT_MS_LANGID_ENGLISH_AUSTRALIA","English","Australia" };
        m[TT_MS_LANGID_ARABIC_YEMEN] = LanguageID{ TT_MS_LANGID_ARABIC_YEMEN, "TT_MS_LANGID_ARABIC_YEMEN","Arabic","Yemen" };
        m[TT_MS_LANGID_ENGLISH_INDIA] = LanguageID{ TT_MS_LANGID_ENGLISH_INDIA, "TT_MS_LANGID_ENGLISH_INDIA","English","India" };
        m[TT_MS_LANGID_ARABIC_SYRIA] = LanguageID{ TT_MS_LANGID_ARABIC_SYRIA, "TT_MS_LANGID_ARABIC_SYRIA","Arabic","Syria" };
        m[TT_MS_LANGID_ENGLISH_MALAYSIA] = LanguageID{ TT_MS_LANGID_ENGLISH_MALAYSIA, "TT_MS_LANGID_ENGLISH_MALAYSIA","English","Malaysia" };
        m[TT_MS_LANGID_ENGLISH_UNITED_STATES] = LanguageID{ TT_MS_LANGID_ENGLISH_UNITED_STATES, "TT_MS_LANGID_ENGLISH_UNITED_STATES","English","United States" };
        m[TT_MS_LANGID_ARABIC_JORDAN] = LanguageID{ TT_MS_LANGID_ARABIC_JORDAN, "TT_MS_LANGID_ARABIC_JORDAN","Arabic","Jordan" };
        m[TT_MS_LANGID_ENGLISH_IRELAND] = LanguageID{ TT_MS_LANGID_ENGLISH_IRELAND, "TT_MS_LANGID_ENGLISH_IRELAND","English","Ireland" };
        m[TT_MS_LANGID_ARABIC_LEBANON] = LanguageID{ TT_MS_LANGID_ARABIC_LEBANON, "TT_MS_LANGID_ARABIC_LEBANON","Arabic","Lebanon" };
        m[TT_MS_LANGID_ENGLISH_SOUTH_AFRICA] = LanguageID{ TT_MS_LANGID_ENGLISH_SOUTH_AFRICA, "TT_MS_LANGID_ENGLISH_SOUTH_AFRICA","English","South Africa" };
        m[TT_MS_LANGID_TIBETAN_PRC] = LanguageID{ TT_MS_LANGID_TIBETAN_PRC, "TT_MS_LANGID_TIBETAN_PRC","Tibetan","PRC" };
        m[TT_MS_LANGID_ARABIC_KUWAIT] = LanguageID{ TT_MS_LANGID_ARABIC_KUWAIT, "TT_MS_LANGID_ARABIC_KUWAIT","Arabic","Kuwait" };
        m[TT_MS_LANGID_ENGLISH_CANADA] = LanguageID{ TT_MS_LANGID_ENGLISH_CANADA, "TT_MS_LANGID_ENGLISH_CANADA","English","Canada" };
        m[TT_MS_LANGID_ARABIC_UAE] = LanguageID{ TT_MS_LANGID_ARABIC_UAE, "TT_MS_LANGID_ARABIC_UAE","Arabic","U.A.E." };
        m[TT_MS_LANGID_ENGLISH_NEW_ZEALAND] = LanguageID{ TT_MS_LANGID_ENGLISH_NEW_ZEALAND, "TT_MS_LANGID_ENGLISH_NEW_ZEALAND","English","New Zealand" };
        m[TT_MS_LANGID_ARABIC_BAHRAIN] = LanguageID{ TT_MS_LANGID_ARABIC_BAHRAIN, "TT_MS_LANGID_ARABIC_BAHRAIN","Arabic","Bahrain" };
        m[TT_MS_LANGID_ENGLISH_BELIZE] = LanguageID{ TT_MS_LANGID_ENGLISH_BELIZE, "TT_MS_LANGID_ENGLISH_BELIZE","English","Belize" };
        m[TT_MS_LANGID_ARABIC_QATAR] = LanguageID{ TT_MS_LANGID_ARABIC_QATAR, "TT_MS_LANGID_ARABIC_QATAR","Arabic","Qatar" };
        m[TT_MS_LANGID_OCCITAN_FRANCE] = LanguageID{ TT_MS_LANGID_OCCITAN_FRANCE, "TT_MS_LANGID_OCCITAN_FRANCE","Occitan","France" };
        m[TT_MS_LANGID_SPANISH_DOMINICAN_REPUBLIC] = LanguageID{ TT_MS_LANGID_SPANISH_DOMINICAN_REPUBLIC, "TT_MS_LANGID_SPANISH_DOMINICAN_REPUBLIC","Spanish","Dominican Republic" };
        m[TT_MS_LANGID_BULGARIAN_BULGARIA] = LanguageID{ TT_MS_LANGID_BULGARIAN_BULGARIA, "TT_MS_LANGID_BULGARIAN_BULGARIA","Bulgarian","Bulgaria" };
        m[TT_MS_LANGID_GERMAN_LUXEMBOURG] = LanguageID{ TT_MS_LANGID_GERMAN_LUXEMBOURG, "TT_MS_LANGID_GERMAN_LUXEMBOURG","German","Luxembourg" };
        m[TT_MS_LANGID_CATALAN_CATALAN] = LanguageID{ TT_MS_LANGID_CATALAN_CATALAN, "TT_MS_LANGID_CATALAN_CATALAN","Catalan","Catalan" };
        m[TT_MS_LANGID_CHINESE_TAIWAN] = LanguageID{ TT_MS_LANGID_CHINESE_TAIWAN, "TT_MS_LANGID_CHINESE_TAIWAN","Chinese","Taiwan" };
        m[TT_MS_LANGID_CHINESE_PRC] = LanguageID{ TT_MS_LANGID_CHINESE_PRC, "TT_MS_LANGID_CHINESE_PRC","Chinese","People’s Republic of China" };
        m[TT_MS_LANGID_CHINESE_HONG_KONG] = LanguageID{ TT_MS_LANGID_CHINESE_HONG_KONG, "TT_MS_LANGID_CHINESE_HONG_KONG","Chinese","Hong Kong S.A.R." };
        m[TT_MS_LANGID_YI_PRC] = LanguageID{ TT_MS_LANGID_YI_PRC, "TT_MS_LANGID_YI_PRC","Yi","PRC" };
        m[TT_MS_LANGID_CHINESE_SINGAPORE] = LanguageID{ TT_MS_LANGID_CHINESE_SINGAPORE, "TT_MS_LANGID_CHINESE_SINGAPORE","Chinese","Singapore" };
        m[TT_MS_LANGID_LAO_LAOS] = LanguageID{ TT_MS_LANGID_LAO_LAOS, "TT_MS_LANGID_LAO_LAOS","Lao","Lao P.D.R." };
        m[TT_MS_LANGID_ITALIAN_SWITZERLAND] = LanguageID{ TT_MS_LANGID_ITALIAN_SWITZERLAND, "TT_MS_LANGID_ITALIAN_SWITZERLAND","Italian","Switzerland" };
        m[TT_MS_LANGID_CHINESE_MACAO] = LanguageID{ TT_MS_LANGID_CHINESE_MACAO, "TT_MS_LANGID_CHINESE_MACAO","Chinese","Macao S.A.R." };
        m[TT_MS_LANGID_SPANISH_VENEZUELA] = LanguageID{ TT_MS_LANGID_SPANISH_VENEZUELA, "TT_MS_LANGID_SPANISH_VENEZUELA","Spanish","Venezuela" };
        m[TT_MS_LANGID_DANISH_DENMARK] = LanguageID{ TT_MS_LANGID_DANISH_DENMARK, "TT_MS_LANGID_DANISH_DENMARK","Danish","Denmark" };
        m[TT_MS_LANGID_GERMAN_GERMANY] = LanguageID{ TT_MS_LANGID_GERMAN_GERMANY, "TT_MS_LANGID_GERMAN_GERMANY","German","Germany" };
        m[TT_MS_LANGID_ARMENIAN_ARMENIA] = LanguageID{ TT_MS_LANGID_ARMENIAN_ARMENIA, "TT_MS_LANGID_ARMENIAN_ARMENIA","Armenian","Armenia" };
        m[TT_MS_LANGID_GERMAN_SWITZERLAND] = LanguageID{ TT_MS_LANGID_GERMAN_SWITZERLAND, "TT_MS_LANGID_GERMAN_SWITZERLAND","German","Switzerland" };
        m[TT_MS_LANGID_DUTCH_BELGIUM] = LanguageID{ TT_MS_LANGID_DUTCH_BELGIUM, "TT_MS_LANGID_DUTCH_BELGIUM","Dutch","Belgium" };
        m[TT_MS_LANGID_GERMAN_AUSTRIA] = LanguageID{ TT_MS_LANGID_GERMAN_AUSTRIA, "TT_MS_LANGID_GERMAN_AUSTRIA","German","Austria" };
        m[TT_MS_LANGID_GERMAN_LIECHTENSTEIN] = LanguageID{ TT_MS_LANGID_GERMAN_LIECHTENSTEIN, "TT_MS_LANGID_GERMAN_LIECHTENSTEIN","German","Liechtenstein" };
        m[TT_MS_LANGID_IRISH_IRELAND] = LanguageID{ TT_MS_LANGID_IRISH_IRELAND, "TT_MS_LANGID_IRISH_IRELAND","Irish","Ireland" };
        m[TT_MS_LANGID_GREEK_GREECE] = LanguageID{ TT_MS_LANGID_GREEK_GREECE, "TT_MS_LANGID_GREEK_GREECE","Greek","Greece" };
        m[TT_MS_LANGID_SPANISH_EL_SALVADOR] = LanguageID{ TT_MS_LANGID_SPANISH_EL_SALVADOR, "TT_MS_LANGID_SPANISH_EL_SALVADOR","Spanish","El Salvador" };
        m[TT_MS_LANGID_SPANISH_SPAIN_TRADITIONAL_SORT] = LanguageID{ TT_MS_LANGID_SPANISH_SPAIN_TRADITIONAL_SORT, "TT_MS_LANGID_SPANISH_SPAIN_TRADITIONAL_SORT","Spanish (Traditional Sort)","Spain" };
        m[TT_MS_LANGID_MARATHI_INDIA] = LanguageID{ TT_MS_LANGID_MARATHI_INDIA, "TT_MS_LANGID_MARATHI_INDIA","Marathi","India" };
        m[TT_MS_LANGID_SPANISH_MEXICO] = LanguageID{ TT_MS_LANGID_SPANISH_MEXICO, "TT_MS_LANGID_SPANISH_MEXICO","Spanish","Mexico" };
        m[TT_MS_LANGID_SPANISH_SPAIN_MODERN_SORT] = LanguageID{ TT_MS_LANGID_SPANISH_SPAIN_MODERN_SORT, "TT_MS_LANGID_SPANISH_SPAIN_MODERN_SORT","Spanish (Modern Sort)","Spain" };
        m[TT_MS_LANGID_SPANISH_GUATEMALA] = LanguageID{ TT_MS_LANGID_SPANISH_GUATEMALA, "TT_MS_LANGID_SPANISH_GUATEMALA","Spanish","Guatemala" };
        m[TT_MS_LANGID_SYRIAC_SYRIA] = LanguageID{ TT_MS_LANGID_SYRIAC_SYRIA, "TT_MS_LANGID_SYRIAC_SYRIA","Syriac","Syria" };
        m[TT_MS_LANGID_SPANISH_COSTA_RICA] = LanguageID{ TT_MS_LANGID_SPANISH_COSTA_RICA, "TT_MS_LANGID_SPANISH_COSTA_RICA","Spanish","Costa Rica" };
        m[TT_MS_LANGID_SPANISH_PANAMA] = LanguageID{ TT_MS_LANGID_SPANISH_PANAMA, "TT_MS_LANGID_SPANISH_PANAMA","Spanish","Panama" };
        m[TT_MS_LANGID_SPANISH_COLOMBIA] = LanguageID{ TT_MS_LANGID_SPANISH_COLOMBIA, "TT_MS_LANGID_SPANISH_COLOMBIA","Spanish","Colombia" };
        m[TT_MS_LANGID_SPANISH_PERU] = LanguageID{ TT_MS_LANGID_SPANISH_PERU, "TT_MS_LANGID_SPANISH_PERU","Spanish","Peru" };
        m[TT_MS_LANGID_SPANISH_ARGENTINA] = LanguageID{ TT_MS_LANGID_SPANISH_ARGENTINA, "TT_MS_LANGID_SPANISH_ARGENTINA","Spanish","Argentina" };
        m[TT_MS_LANGID_SPANISH_ECUADOR] = LanguageID{ TT_MS_LANGID_SPANISH_ECUADOR, "TT_MS_LANGID_SPANISH_ECUADOR","Spanish","Ecuador" };
        m[TT_MS_LANGID_MALAY_BRUNEI_DARUSSALAM] = LanguageID{ TT_MS_LANGID_MALAY_BRUNEI_DARUSSALAM, "TT_MS_LANGID_MALAY_BRUNEI_DARUSSALAM","Malay","Brunei Darussalam" };
        m[TT_MS_LANGID_SPANISH_CHILE] = LanguageID{ TT_MS_LANGID_SPANISH_CHILE, "TT_MS_LANGID_SPANISH_CHILE","Spanish","Chile" };
        m[TT_MS_LANGID_MALAY_MALAYSIA] = LanguageID{ TT_MS_LANGID_MALAY_MALAYSIA, "TT_MS_LANGID_MALAY_MALAYSIA","Malay","Malaysia" };
        m[TT_MS_LANGID_SPANISH_URUGUAY] = LanguageID{ TT_MS_LANGID_SPANISH_URUGUAY, "TT_MS_LANGID_SPANISH_URUGUAY","Spanish","Uruguay" };
        m[TT_MS_LANGID_UKRAINIAN_UKRAINE] = LanguageID{ TT_MS_LANGID_UKRAINIAN_UKRAINE, "TT_MS_LANGID_UKRAINIAN_UKRAINE","Ukrainian","Ukraine" };
        m[TT_MS_LANGID_SPANISH_PARAGUAY] = LanguageID{ TT_MS_LANGID_SPANISH_PARAGUAY, "TT_MS_LANGID_SPANISH_PARAGUAY","Spanish","Paraguay" };
        m[TT_MS_LANGID_SPANISH_BOLIVIA] = LanguageID{ TT_MS_LANGID_SPANISH_BOLIVIA, "TT_MS_LANGID_SPANISH_BOLIVIA","Spanish","Bolivia" };
        m[TT_MS_LANGID_SPANISH_HONDURAS] = LanguageID{ TT_MS_LANGID_SPANISH_HONDURAS, "TT_MS_LANGID_SPANISH_HONDURAS","Spanish","Honduras" };
        m[TT_MS_LANGID_SPANISH_NICARAGUA] = LanguageID{ TT_MS_LANGID_SPANISH_NICARAGUA, "TT_MS_LANGID_SPANISH_NICARAGUA","Spanish","Nicaragua" };
        m[TT_MS_LANGID_SPANISH_PUERTO_RICO] = LanguageID{ TT_MS_LANGID_SPANISH_PUERTO_RICO, "TT_MS_LANGID_SPANISH_PUERTO_RICO","Spanish","Puerto Rico" };
        m[TT_MS_LANGID_SPANISH_UNITED_STATES] = LanguageID{ TT_MS_LANGID_SPANISH_UNITED_STATES, "TT_MS_LANGID_SPANISH_UNITED_STATES","Spanish","United States" };
        m[TT_MS_LANGID_FINNISH_FINLAND] = LanguageID{ TT_MS_LANGID_FINNISH_FINLAND, "TT_MS_LANGID_FINNISH_FINLAND","Finnish","Finland" };
        m[TT_MS_LANGID_FRENCH_FRANCE] = LanguageID{ TT_MS_LANGID_FRENCH_FRANCE, "TT_MS_LANGID_FRENCH_FRANCE","French","France" };
        m[TT_MS_LANGID_ROMANIAN_ROMANIA] = LanguageID{ TT_MS_LANGID_ROMANIAN_ROMANIA, "TT_MS_LANGID_ROMANIAN_ROMANIA","Romanian","Romania" };
        m[TT_MS_LANGID_FRENCH_BELGIUM] = LanguageID{ TT_MS_LANGID_FRENCH_BELGIUM, "TT_MS_LANGID_FRENCH_BELGIUM","French","Belgium" };
        m[TT_MS_LANGID_FRENCH_CANADA] = LanguageID{ TT_MS_LANGID_FRENCH_CANADA, "TT_MS_LANGID_FRENCH_CANADA","French","Canada" };
        m[TT_MS_LANGID_FRENCH_SWITZERLAND] = LanguageID{ TT_MS_LANGID_FRENCH_SWITZERLAND, "TT_MS_LANGID_FRENCH_SWITZERLAND","French","Switzerland" };
        m[TT_MS_LANGID_FRENCH_LUXEMBOURG] = LanguageID{ TT_MS_LANGID_FRENCH_LUXEMBOURG, "TT_MS_LANGID_FRENCH_LUXEMBOURG","French","Luxembourg" };
        m[TT_MS_LANGID_FRENCH_MONACO] = LanguageID{ TT_MS_LANGID_FRENCH_MONACO, "TT_MS_LANGID_FRENCH_MONACO","French","Principality of Monoco" };
        m[TT_MS_LANGID_HEBREW_ISRAEL] = LanguageID{ TT_MS_LANGID_HEBREW_ISRAEL, "TT_MS_LANGID_HEBREW_ISRAEL","Hebrew","Israel" };
        m[TT_MS_LANGID_SERBIAN_BOSNIA_HERZ_LATIN] = LanguageID{ TT_MS_LANGID_SERBIAN_BOSNIA_HERZ_LATIN, "TT_MS_LANGID_SERBIAN_BOSNIA_HERZ_LATIN","Serbian (Latin)","Bosnia and Herzegovina" };
        m[TT_MS_LANGID_HUNGARIAN_HUNGARY] = LanguageID{ TT_MS_LANGID_HUNGARIAN_HUNGARY, "TT_MS_LANGID_HUNGARIAN_HUNGARY","Hungarian","Hungary" };
        m[TT_MS_LANGID_UZBEK_UZBEKISTAN_CYRILLIC] = LanguageID{ TT_MS_LANGID_UZBEK_UZBEKISTAN_CYRILLIC, "TT_MS_LANGID_UZBEK_UZBEKISTAN_CYRILLIC","Uzbek (Cyrillic)","Uzbekistan" };
        m[TT_MS_LANGID_ICELANDIC_ICELAND] = LanguageID{ TT_MS_LANGID_ICELANDIC_ICELAND, "TT_MS_LANGID_ICELANDIC_ICELAND","Icelandic","Iceland" };
        m[TT_MS_LANGID_ITALIAN_ITALY] = LanguageID{ TT_MS_LANGID_ITALIAN_ITALY, "TT_MS_LANGID_ITALIAN_ITALY","Italian","Italy" };
        m[TT_MS_LANGID_JAPANESE_JAPAN] = LanguageID{ TT_MS_LANGID_JAPANESE_JAPAN, "TT_MS_LANGID_JAPANESE_JAPAN","Japanese","Japan" };
        m[TT_MS_LANGID_KOREAN_KOREA] = LanguageID{ TT_MS_LANGID_KOREAN_KOREA, "TT_MS_LANGID_KOREAN_KOREA","Korean","Korea" };
        m[TT_MS_LANGID_DUTCH_NETHERLANDS] = LanguageID{ TT_MS_LANGID_DUTCH_NETHERLANDS, "TT_MS_LANGID_DUTCH_NETHERLANDS","Dutch","Netherlands" };
        m[TT_MS_LANGID_NORWEGIAN_NORWAY_BOKMAL] = LanguageID{ TT_MS_LANGID_NORWEGIAN_NORWAY_BOKMAL, "TT_MS_LANGID_NORWEGIAN_NORWAY_BOKMAL","Norwegian (Bokmal)","Norway" };
        m[TT_MS_LANGID_NORWEGIAN_NORWAY_NYNORSK] = LanguageID{ TT_MS_LANGID_NORWEGIAN_NORWAY_NYNORSK, "TT_MS_LANGID_NORWEGIAN_NORWAY_NYNORSK","Norwegian (Nynorsk)","Norway" };
        m[TT_MS_LANGID_POLISH_POLAND] = LanguageID{ TT_MS_LANGID_POLISH_POLAND, "TT_MS_LANGID_POLISH_POLAND","Polish","Poland" };
        m[TT_MS_LANGID_PORTUGUESE_BRAZIL] = LanguageID{ TT_MS_LANGID_PORTUGUESE_BRAZIL, "TT_MS_LANGID_PORTUGUESE_BRAZIL","Portuguese","Brazil" };
        m[TT_MS_LANGID_FRISIAN_NETHERLANDS] = LanguageID{ TT_MS_LANGID_FRISIAN_NETHERLANDS, "TT_MS_LANGID_FRISIAN_NETHERLANDS","Frisian","Netherlands" };
        m[TT_MS_LANGID_PORTUGUESE_PORTUGAL] = LanguageID{ TT_MS_LANGID_PORTUGUESE_PORTUGAL, "TT_MS_LANGID_PORTUGUESE_PORTUGAL","Portuguese","Portugal" };
        m[TT_MS_LANGID_SAMI_NORTHERN_SWEDEN] = LanguageID{ TT_MS_LANGID_SAMI_NORTHERN_SWEDEN, "TT_MS_LANGID_SAMI_NORTHERN_SWEDEN","Sami (Northern)","Sweden" };
        m[TT_MS_LANGID_ROMANSH_SWITZERLAND] = LanguageID{ TT_MS_LANGID_ROMANSH_SWITZERLAND, "TT_MS_LANGID_ROMANSH_SWITZERLAND","Romansh","Switzerland" };
        m[TT_MS_LANGID_RUSSIAN_RUSSIA] = LanguageID{ TT_MS_LANGID_RUSSIAN_RUSSIA, "TT_MS_LANGID_RUSSIAN_RUSSIA","Russian","Russia" };
        m[TT_MS_LANGID_CROATIAN_CROATIA] = LanguageID{ TT_MS_LANGID_CROATIAN_CROATIA, "TT_MS_LANGID_CROATIAN_CROATIA","Croatian","Croatia" };
        m[TT_MS_LANGID_AMHARIC_ETHIOPIA] = LanguageID{ TT_MS_LANGID_AMHARIC_ETHIOPIA, "TT_MS_LANGID_AMHARIC_ETHIOPIA","Amharic","Ethiopia" };
        m[TT_MS_LANGID_SERBIAN_SERBIA_LATIN] = LanguageID{ TT_MS_LANGID_SERBIAN_SERBIA_LATIN, "TT_MS_LANGID_SERBIAN_SERBIA_LATIN","Serbian (Latin)","Serbia" };
        m[TT_MS_LANGID_TURKMEN_TURKMENISTAN] = LanguageID{ TT_MS_LANGID_TURKMEN_TURKMENISTAN, "TT_MS_LANGID_TURKMEN_TURKMENISTAN","Turkmen","Turkmenistan" };
        m[TT_MS_LANGID_SERBIAN_SERBIA_CYRILLIC] = LanguageID{ TT_MS_LANGID_SERBIAN_SERBIA_CYRILLIC, "TT_MS_LANGID_SERBIAN_SERBIA_CYRILLIC","Serbian (Cyrillic)","Serbia" };
        m[TT_MS_LANGID_LATVIAN_LATVIA] = LanguageID{ TT_MS_LANGID_LATVIAN_LATVIA, "TT_MS_LANGID_LATVIAN_LATVIA","Latvian","Latvia" };
        m[TT_MS_LANGID_CROATIAN_BOSNIA_HERZEGOVINA] = LanguageID{ TT_MS_LANGID_CROATIAN_BOSNIA_HERZEGOVINA, "TT_MS_LANGID_CROATIAN_BOSNIA_HERZEGOVINA","Croatian (Latin)","Bosnia and Herzegovina" };
        m[TT_MS_LANGID_BOSNIAN_BOSNIA_HERZEGOVINA] = LanguageID{ TT_MS_LANGID_BOSNIAN_BOSNIA_HERZEGOVINA, "TT_MS_LANGID_BOSNIAN_BOSNIA_HERZEGOVINA","Bosnian (Latin)","Bosnia and Herzegovina" };
        m[TT_MS_LANGID_SETSWANA_SOUTH_AFRICA] = LanguageID{ TT_MS_LANGID_SETSWANA_SOUTH_AFRICA, "TT_MS_LANGID_SETSWANA_SOUTH_AFRICA","Setswana","South Africa" };
        m[TT_MS_LANGID_SERBIAN_BOSNIA_HERZ_CYRILLIC] = LanguageID{ TT_MS_LANGID_SERBIAN_BOSNIA_HERZ_CYRILLIC, "TT_MS_LANGID_SERBIAN_BOSNIA_HERZ_CYRILLIC","Serbian (Cyrillic)","Bosnia and Herzegovina" };
        m[TT_MS_LANGID_BOSNIAN_BOSNIA_HERZ_CYRILLIC] = LanguageID{ TT_MS_LANGID_BOSNIAN_BOSNIA_HERZ_CYRILLIC, "TT_MS_LANGID_BOSNIAN_BOSNIA_HERZ_CYRILLIC","Bosnian (Cyrillic)","Bosnia and Herzegovina" };
        m[TT_MS_LANGID_SLOVAK_SLOVAKIA] = LanguageID{ TT_MS_LANGID_SLOVAK_SLOVAKIA, "TT_MS_LANGID_SLOVAK_SLOVAKIA","Slovak","Slovakia" };
        m[TT_MS_LANGID_ALBANIAN_ALBANIA] = LanguageID{ TT_MS_LANGID_ALBANIAN_ALBANIA, "TT_MS_LANGID_ALBANIAN_ALBANIA","Albanian","Albania" };
        m[TT_MS_LANGID_SWEDISH_SWEDEN] = LanguageID{ TT_MS_LANGID_SWEDISH_SWEDEN, "TT_MS_LANGID_SWEDISH_SWEDEN","Swedish","Sweden" };
        m[TT_MS_LANGID_THAI_THAILAND] = LanguageID{ TT_MS_LANGID_THAI_THAILAND, "TT_MS_LANGID_THAI_THAILAND","Thai","Thailand" };
        m[TT_MS_LANGID_TURKISH_TURKEY] = LanguageID{ TT_MS_LANGID_TURKISH_TURKEY, "TT_MS_LANGID_TURKISH_TURKEY","Turkish","Turkey" };
        m[TT_MS_LANGID_URDU_PAKISTAN] = LanguageID{ TT_MS_LANGID_URDU_PAKISTAN, "TT_MS_LANGID_URDU_PAKISTAN","Urdu","Islamic Republic of Pakistan" };
        m[TT_MS_LANGID_BELARUSIAN_BELARUS] = LanguageID{ TT_MS_LANGID_BELARUSIAN_BELARUS, "TT_MS_LANGID_BELARUSIAN_BELARUS","Belarusian","Belarus" };
        m[TT_MS_LANGID_SLOVENIAN_SLOVENIA] = LanguageID{ TT_MS_LANGID_SLOVENIAN_SLOVENIA, "TT_MS_LANGID_SLOVENIAN_SLOVENIA","Slovenian","Slovenia" };
        m[TT_MS_LANGID_ESTONIAN_ESTONIA] = LanguageID{ TT_MS_LANGID_ESTONIAN_ESTONIA, "TT_MS_LANGID_ESTONIAN_ESTONIA","Estonian","Estonia" };
        m[TT_MS_LANGID_LITHUANIAN_LITHUANIA] = LanguageID{ TT_MS_LANGID_LITHUANIAN_LITHUANIA, "TT_MS_LANGID_LITHUANIAN_LITHUANIA","Lithuanian","Lithuania" };
        m[TT_MS_LANGID_TAJIK_TAJIKISTAN] = LanguageID{ TT_MS_LANGID_TAJIK_TAJIKISTAN, "TT_MS_LANGID_TAJIK_TAJIKISTAN","Tajik (Cyrillic)","Tajikistan" };
        m[TT_MS_LANGID_VIETNAMESE_VIET_NAM] = LanguageID{ TT_MS_LANGID_VIETNAMESE_VIET_NAM, "TT_MS_LANGID_VIETNAMESE_VIET_NAM","Vietnamese","Vietnam" };
        m[TT_MS_LANGID_AZERI_AZERBAIJAN_LATIN] = LanguageID{ TT_MS_LANGID_AZERI_AZERBAIJAN_LATIN, "TT_MS_LANGID_AZERI_AZERBAIJAN_LATIN","Azeri (Latin)","Azerbaijan" };
        m[TT_MS_LANGID_FAEROESE_FAEROE_ISLANDS] = LanguageID{ TT_MS_LANGID_FAEROESE_FAEROE_ISLANDS, "TT_MS_LANGID_FAEROESE_FAEROE_ISLANDS","Faroese","Faroe Islands" };
        m[TT_MS_LANGID_AZERI_AZERBAIJAN_CYRILLIC] = LanguageID{ TT_MS_LANGID_AZERI_AZERBAIJAN_CYRILLIC, "TT_MS_LANGID_AZERI_AZERBAIJAN_CYRILLIC","Azeri (Cyrillic)","Azerbaijan" };
        m[TT_MS_LANGID_BASQUE_BASQUE] = LanguageID{ TT_MS_LANGID_BASQUE_BASQUE, "TT_MS_LANGID_BASQUE_BASQUE","Basque","Basque" };
        m[TT_MS_LANGID_UPPER_SORBIAN_GERMANY] = LanguageID{ TT_MS_LANGID_UPPER_SORBIAN_GERMANY, "TT_MS_LANGID_UPPER_SORBIAN_GERMANY","Upper Sorbian","Germany" };
        m[TT_MS_LANGID_LOWER_SORBIAN_GERMANY] = LanguageID{ TT_MS_LANGID_LOWER_SORBIAN_GERMANY, "TT_MS_LANGID_LOWER_SORBIAN_GERMANY","Lower Sorbian","Germany" };
        m[TT_MS_LANGID_MACEDONIAN_MACEDONIA] = LanguageID{ TT_MS_LANGID_MACEDONIAN_MACEDONIA, "TT_MS_LANGID_MACEDONIAN_MACEDONIA","Macedonian (FYROM)","Former Yugoslav Republic of Macedonia" };
        m[TT_MS_LANGID_ISIXHOSA_SOUTH_AFRICA] = LanguageID{ TT_MS_LANGID_ISIXHOSA_SOUTH_AFRICA, "TT_MS_LANGID_ISIXHOSA_SOUTH_AFRICA","isiXhosa","South Africa" };
        m[TT_MS_LANGID_AFRIKAANS_SOUTH_AFRICA] = LanguageID{ TT_MS_LANGID_AFRIKAANS_SOUTH_AFRICA, "TT_MS_LANGID_AFRIKAANS_SOUTH_AFRICA","Afrikaans","South Africa" };
        m[TT_MS_LANGID_GEORGIAN_GEORGIA] = LanguageID{ TT_MS_LANGID_GEORGIAN_GEORGIA, "TT_MS_LANGID_GEORGIAN_GEORGIA","Georgian","Georgia" };
        m[TT_MS_LANGID_HINDI_INDIA] = LanguageID{ TT_MS_LANGID_HINDI_INDIA, "TT_MS_LANGID_HINDI_INDIA","Hindi","India" };
        m[TT_MS_LANGID_MALTESE_MALTA] = LanguageID{ TT_MS_LANGID_MALTESE_MALTA, "TT_MS_LANGID_MALTESE_MALTA","Maltese","Malta" };
        m[TT_MS_LANGID_SAMI_NORTHERN_NORWAY] = LanguageID{ TT_MS_LANGID_SAMI_NORTHERN_NORWAY, "TT_MS_LANGID_SAMI_NORTHERN_NORWAY","Sami (Northern)","Norway" };
        m[TT_MS_LANGID_PASHTO_AFGHANISTAN] = LanguageID{ TT_MS_LANGID_PASHTO_AFGHANISTAN, "TT_MS_LANGID_PASHTO_AFGHANISTAN","Pashto","Afghanistan" };
        m[TT_MS_LANGID_SAMI_NORTHERN_FINLAND] = LanguageID{ TT_MS_LANGID_SAMI_NORTHERN_FINLAND, "TT_MS_LANGID_SAMI_NORTHERN_FINLAND","Sami (Northern)","Finland" };
        m[TT_MS_LANGID_SAMI_LULE_NORWAY] = LanguageID{ TT_MS_LANGID_SAMI_LULE_NORWAY, "TT_MS_LANGID_SAMI_LULE_NORWAY","Sami (Lule)","Norway" };
        m[TT_MS_LANGID_KANNADA_INDIA] = LanguageID{ TT_MS_LANGID_KANNADA_INDIA, "TT_MS_LANGID_KANNADA_INDIA","Kannada","India" };
        m[TT_MS_LANGID_SAMI_LULE_SWEDEN] = LanguageID{ TT_MS_LANGID_SAMI_LULE_SWEDEN, "TT_MS_LANGID_SAMI_LULE_SWEDEN","Sami (Lule)","Sweden" };
        m[TT_MS_LANGID_SAMI_SOUTHERN_NORWAY] = LanguageID{ TT_MS_LANGID_SAMI_SOUTHERN_NORWAY, "TT_MS_LANGID_SAMI_SOUTHERN_NORWAY","Sami (Southern)","Norway" };
        m[TT_MS_LANGID_SAMI_SOUTHERN_SWEDEN] = LanguageID{ TT_MS_LANGID_SAMI_SOUTHERN_SWEDEN, "TT_MS_LANGID_SAMI_SOUTHERN_SWEDEN","Sami (Southern)","Sweden" };
        m[TT_MS_LANGID_SAMI_SKOLT_FINLAND] = LanguageID{ TT_MS_LANGID_SAMI_SKOLT_FINLAND, "TT_MS_LANGID_SAMI_SKOLT_FINLAND","Sami (Skolt)","Finland" };
        m[TT_MS_LANGID_SAMI_INARI_FINLAND] = LanguageID{ TT_MS_LANGID_SAMI_INARI_FINLAND, "TT_MS_LANGID_SAMI_INARI_FINLAND","Sami (Inari)","Finland" };
        m[TT_MS_LANGID_KAZAKH_KAZAKHSTAN] = LanguageID{ TT_MS_LANGID_KAZAKH_KAZAKHSTAN, "TT_MS_LANGID_KAZAKH_KAZAKHSTAN","Kazakh","Kazakhstan" };
        m[TT_MS_LANGID_KYRGYZ_KYRGYZSTAN] = LanguageID{ TT_MS_LANGID_KYRGYZ_KYRGYZSTAN, "TT_MS_LANGID_KYRGYZ_KYRGYZSTAN","Kyrgyz","Kyrgyzstan" };
        m[TT_MS_LANGID_KISWAHILI_KENYA] = LanguageID{ TT_MS_LANGID_KISWAHILI_KENYA, "TT_MS_LANGID_KISWAHILI_KENYA","Kiswahili","Kenya" };
        m[TT_MS_LANGID_UZBEK_UZBEKISTAN_LATIN] = LanguageID{ TT_MS_LANGID_UZBEK_UZBEKISTAN_LATIN, "TT_MS_LANGID_UZBEK_UZBEKISTAN_LATIN","Uzbek (Latin)","Uzbekistan" };
        m[TT_MS_LANGID_TATAR_RUSSIA] = LanguageID{ TT_MS_LANGID_TATAR_RUSSIA, "TT_MS_LANGID_TATAR_RUSSIA","Tatar","Russia" };
        m[TT_MS_LANGID_PUNJABI_INDIA] = LanguageID{ TT_MS_LANGID_PUNJABI_INDIA, "TT_MS_LANGID_PUNJABI_INDIA","Punjabi","India" };
        m[TT_MS_LANGID_QUECHUA_ECUADOR] = LanguageID{ TT_MS_LANGID_QUECHUA_ECUADOR, "TT_MS_LANGID_QUECHUA_ECUADOR","Quechua","Ecuador" };
        m[TT_MS_LANGID_GUJARATI_INDIA] = LanguageID{ TT_MS_LANGID_GUJARATI_INDIA, "TT_MS_LANGID_GUJARATI_INDIA","Gujarati","India" };
        m[TT_MS_LANGID_ODIA_INDIA] = LanguageID{ TT_MS_LANGID_ODIA_INDIA, "TT_MS_LANGID_ODIA_INDIA","Odia (formerly Oriya)","India" };
        m[TT_MS_LANGID_TAMIL_INDIA] = LanguageID{ TT_MS_LANGID_TAMIL_INDIA, "TT_MS_LANGID_TAMIL_INDIA","Tamil","India" };
        m[TT_MS_LANGID_TELUGU_INDIA] = LanguageID{ TT_MS_LANGID_TELUGU_INDIA, "TT_MS_LANGID_TELUGU_INDIA","Telugu","India" };
        m[TT_MS_LANGID_MALAYALAM_INDIA] = LanguageID{ TT_MS_LANGID_MALAYALAM_INDIA, "TT_MS_LANGID_MALAYALAM_INDIA","Malayalam","India" };
        m[TT_MS_LANGID_ASSAMESE_INDIA] = LanguageID{ TT_MS_LANGID_ASSAMESE_INDIA, "TT_MS_LANGID_ASSAMESE_INDIA","Assamese","India" };
        m[TT_MS_LANGID_SANSKRIT_INDIA] = LanguageID{ TT_MS_LANGID_SANSKRIT_INDIA, "TT_MS_LANGID_SANSKRIT_INDIA","Sanskrit","India" };
        m[TT_MS_LANGID_MONGOLIAN_MONGOLIA] = LanguageID{ TT_MS_LANGID_MONGOLIAN_MONGOLIA, "TT_MS_LANGID_MONGOLIAN_MONGOLIA","Mongolian (Cyrillic)","Mongolia" };
        m[TT_MS_LANGID_MONGOLIAN_PRC] = LanguageID{ TT_MS_LANGID_MONGOLIAN_PRC, "TT_MS_LANGID_MONGOLIAN_PRC","Mongolian (Traditional)","People’s Republic of China" };
        m[TT_MS_LANGID_WELSH_UNITED_KINGDOM] = LanguageID{ TT_MS_LANGID_WELSH_UNITED_KINGDOM, "TT_MS_LANGID_WELSH_UNITED_KINGDOM","Welsh","United Kingdom" };
        m[TT_MS_LANGID_QUECHUA_PERU] = LanguageID{ TT_MS_LANGID_QUECHUA_PERU, "TT_MS_LANGID_QUECHUA_PERU","Quechua","Peru" };
        m[TT_MS_LANGID_KHMER_CAMBODIA] = LanguageID{ TT_MS_LANGID_KHMER_CAMBODIA, "TT_MS_LANGID_KHMER_CAMBODIA","Khmer","Cambodia" };
        m[TT_MS_LANGID_GALICIAN_GALICIAN] = LanguageID{ TT_MS_LANGID_GALICIAN_GALICIAN, "TT_MS_LANGID_GALICIAN_GALICIAN","Galician","Galician" };
        m[TT_MS_LANGID_KONKANI_INDIA] = LanguageID{ TT_MS_LANGID_KONKANI_INDIA, "TT_MS_LANGID_KONKANI_INDIA","Konkani","India" };
        m[TT_MS_LANGID_SINHALA_SRI_LANKA] = LanguageID{ TT_MS_LANGID_SINHALA_SRI_LANKA, "TT_MS_LANGID_SINHALA_SRI_LANKA","Sinhala","Sri Lanka" };
        m[TT_MS_LANGID_INUKTITUT_CANADA] = LanguageID{ TT_MS_LANGID_INUKTITUT_CANADA, "TT_MS_LANGID_INUKTITUT_CANADA","Inuktitut","Canada" };
        m[TT_MS_LANGID_INUKTITUT_CANADA_LATIN] = LanguageID{ TT_MS_LANGID_INUKTITUT_CANADA_LATIN, "TT_MS_LANGID_INUKTITUT_CANADA_LATIN","Inuktitut (Latin)","Canada" };
        m[TT_MS_LANGID_TAMAZIGHT_ALGERIA] = LanguageID{ TT_MS_LANGID_TAMAZIGHT_ALGERIA, "TT_MS_LANGID_TAMAZIGHT_ALGERIA","Tamazight (Latin)","Algeria" };
        m[TT_MS_LANGID_NEPALI_NEPAL] = LanguageID{ TT_MS_LANGID_NEPALI_NEPAL, "TT_MS_LANGID_NEPALI_NEPAL","Nepali","Nepal" };
        m[TT_MS_LANGID_FILIPINO_PHILIPPINES] = LanguageID{ TT_MS_LANGID_FILIPINO_PHILIPPINES, "TT_MS_LANGID_FILIPINO_PHILIPPINES","Filipino","Philippines" };
        m[TT_MS_LANGID_DHIVEHI_MALDIVES] = LanguageID{ TT_MS_LANGID_DHIVEHI_MALDIVES, "TT_MS_LANGID_DHIVEHI_MALDIVES","Divehi","Maldives" };
        m[TT_MS_LANGID_HAUSA_NIGERIA] = LanguageID{ TT_MS_LANGID_HAUSA_NIGERIA, "TT_MS_LANGID_HAUSA_NIGERIA","Hausa (Latin)","Nigeria" };
        m[TT_MS_LANGID_YORUBA_NIGERIA] = LanguageID{ TT_MS_LANGID_YORUBA_NIGERIA, "TT_MS_LANGID_YORUBA_NIGERIA","Yoruba","Nigeria" };
        m[TT_MS_LANGID_QUECHUA_BOLIVIA] = LanguageID{ TT_MS_LANGID_QUECHUA_BOLIVIA, "TT_MS_LANGID_QUECHUA_BOLIVIA","Quechua","Bolivia" };
        m[TT_MS_LANGID_SESOTHO_SA_LEBOA_SOUTH_AFRICA] = LanguageID{ TT_MS_LANGID_SESOTHO_SA_LEBOA_SOUTH_AFRICA, "TT_MS_LANGID_SESOTHO_SA_LEBOA_SOUTH_AFRICA","Sesotho sa Leboa","South Africa" };
        m[TT_MS_LANGID_BASHKIR_RUSSIA] = LanguageID{ TT_MS_LANGID_BASHKIR_RUSSIA, "TT_MS_LANGID_BASHKIR_RUSSIA","Bashkir","Russia" };
        m[TT_MS_LANGID_LUXEMBOURGISH_LUXEMBOURG] = LanguageID{ TT_MS_LANGID_LUXEMBOURGISH_LUXEMBOURG, "TT_MS_LANGID_LUXEMBOURGISH_LUXEMBOURG","Luxembourgish","Luxembourg" };
        m[TT_MS_LANGID_GREENLANDIC_GREENLAND] = LanguageID{ TT_MS_LANGID_GREENLANDIC_GREENLAND, "TT_MS_LANGID_GREENLANDIC_GREENLAND","Greenlandic","Greenland" };
        m[TT_MS_LANGID_IGBO_NIGERIA] = LanguageID{ TT_MS_LANGID_IGBO_NIGERIA, "TT_MS_LANGID_IGBO_NIGERIA","Igbo","Nigeria" };
        m[TT_MS_LANGID_MAPUDUNGUN_CHILE] = LanguageID{ TT_MS_LANGID_MAPUDUNGUN_CHILE, "TT_MS_LANGID_MAPUDUNGUN_CHILE","Mapudungun","Chile" };
        m[TT_MS_LANGID_MOHAWK_MOHAWK] = LanguageID{ TT_MS_LANGID_MOHAWK_MOHAWK, "TT_MS_LANGID_MOHAWK_MOHAWK","Mohawk","Mohawk" };
        m[TT_MS_LANGID_BRETON_FRANCE] = LanguageID{ TT_MS_LANGID_BRETON_FRANCE, "TT_MS_LANGID_BRETON_FRANCE","Breton","France" };
        m[TT_MS_LANGID_UIGHUR_PRC] = LanguageID{ TT_MS_LANGID_UIGHUR_PRC, "TT_MS_LANGID_UIGHUR_PRC","Uighur","PRC" };
        m[TT_MS_LANGID_MAORI_NEW_ZEALAND] = LanguageID{ TT_MS_LANGID_MAORI_NEW_ZEALAND, "TT_MS_LANGID_MAORI_NEW_ZEALAND","Maori","New Zealand" };
        m[TT_MS_LANGID_CORSICAN_FRANCE] = LanguageID{ TT_MS_LANGID_CORSICAN_FRANCE, "TT_MS_LANGID_CORSICAN_FRANCE","Corsican","France" };
        m[TT_MS_LANGID_ALSATIAN_FRANCE] = LanguageID{ TT_MS_LANGID_ALSATIAN_FRANCE, "TT_MS_LANGID_ALSATIAN_FRANCE","Alsatian","France" };
        m[TT_MS_LANGID_YAKUT_RUSSIA] = LanguageID{ TT_MS_LANGID_YAKUT_RUSSIA, "TT_MS_LANGID_YAKUT_RUSSIA","Yakut","Russia" };
        m[TT_MS_LANGID_KICHE_GUATEMALA] = LanguageID{ TT_MS_LANGID_KICHE_GUATEMALA, "TT_MS_LANGID_KICHE_GUATEMALA","K’iche","Guatemala" };
        m[TT_MS_LANGID_KINYARWANDA_RWANDA] = LanguageID{ TT_MS_LANGID_KINYARWANDA_RWANDA, "TT_MS_LANGID_KINYARWANDA_RWANDA","Kinyarwanda","Rwanda" };
        m[TT_MS_LANGID_WOLOF_SENEGAL] = LanguageID{ TT_MS_LANGID_WOLOF_SENEGAL, "TT_MS_LANGID_WOLOF_SENEGAL","Wolof","Senegal" };
        m[TT_MS_LANGID_DARI_AFGHANISTAN] = LanguageID{ TT_MS_LANGID_DARI_AFGHANISTAN, "TT_MS_LANGID_DARI_AFGHANISTAN","Dari","Afghanistan" };
    
    }
    
    
    void create_name_id_list(std::unordered_map<unsigned int, NameID>& m) {
    
        m[TT_NAME_ID_COPYRIGHT] = NameID{ TT_NAME_ID_COPYRIGHT      ,"TT_NAME_ID_COPYRIGHT" };
        m[TT_NAME_ID_FONT_FAMILY] = NameID{ TT_NAME_ID_FONT_FAMILY    ,"TT_NAME_ID_FONT_FAMILY" };
        m[TT_NAME_ID_FONT_SUBFAMILY] = NameID{ TT_NAME_ID_FONT_SUBFAMILY ,"TT_NAME_ID_FONT_SUBFAMILY" };
        m[TT_NAME_ID_UNIQUE_ID] = NameID{ TT_NAME_ID_UNIQUE_ID      ,"TT_NAME_ID_UNIQUE_ID" };
        m[TT_NAME_ID_FULL_NAME] = NameID{ TT_NAME_ID_FULL_NAME      ,"TT_NAME_ID_FULL_NAME" };
        m[TT_NAME_ID_VERSION_STRING] = NameID{ TT_NAME_ID_VERSION_STRING ,"TT_NAME_ID_VERSION_STRING" };
        m[TT_NAME_ID_PS_NAME] = NameID{ TT_NAME_ID_PS_NAME        ,"TT_NAME_ID_PS_NAME" };
        m[TT_NAME_ID_TRADEMARK] = NameID{ TT_NAME_ID_TRADEMARK      ,"TT_NAME_ID_TRADEMARK" };
    
        /* the following values are from the OpenType spec */
        m[TT_NAME_ID_MANUFACTURER] = NameID{ TT_NAME_ID_MANUFACTURER,"TT_NAME_ID_MANUFACTURER" };
        m[TT_NAME_ID_DESIGNER] = NameID{ TT_NAME_ID_DESIGNER    ,"TT_NAME_ID_DESIGNER" };
        m[TT_NAME_ID_DESCRIPTION] = NameID{ TT_NAME_ID_DESCRIPTION ,"TT_NAME_ID_DESCRIPTION" };
        m[TT_NAME_ID_VENDOR_URL] = NameID{ TT_NAME_ID_VENDOR_URL  ,"TT_NAME_ID_VENDOR_URL" };
        m[TT_NAME_ID_DESIGNER_URL] = NameID{ TT_NAME_ID_DESIGNER_URL,"TT_NAME_ID_DESIGNER_URL" };
        m[TT_NAME_ID_LICENSE] = NameID{ TT_NAME_ID_LICENSE     ,"TT_NAME_ID_LICENSE" };
        m[TT_NAME_ID_LICENSE_URL] = NameID{ TT_NAME_ID_LICENSE_URL ,"TT_NAME_ID_LICENSE_URL" };
    
        /* number 15 is reserved */
        m[TT_NAME_ID_TYPOGRAPHIC_FAMILY] = NameID{ TT_NAME_ID_TYPOGRAPHIC_FAMILY   ,"TT_NAME_ID_TYPOGRAPHIC_FAMILY" };
        m[TT_NAME_ID_TYPOGRAPHIC_SUBFAMILY] = NameID{ TT_NAME_ID_TYPOGRAPHIC_SUBFAMILY,"TT_NAME_ID_TYPOGRAPHIC_SUBFAMILY" };
        m[TT_NAME_ID_MAC_FULL_NAME] = NameID{ TT_NAME_ID_MAC_FULL_NAME        ,"TT_NAME_ID_MAC_FULL_NAME" };
    
        /* The following code is new as of 2000-01-21 */
        m[TT_NAME_ID_SAMPLE_TEXT] = NameID{ TT_NAME_ID_SAMPLE_TEXT,"TT_NAME_ID_SAMPLE_TEXT" };
    
        /* This is new in OpenType 1.3 */
        m[TT_NAME_ID_CID_FINDFONT_NAME] = NameID{ TT_NAME_ID_CID_FINDFONT_NAME,"TT_NAME_ID_CID_FINDFONT_NAME" };
    
        /* This is new in OpenType 1.5 */
        m[TT_NAME_ID_WWS_FAMILY] = NameID{ TT_NAME_ID_WWS_FAMILY,"TT_NAME_ID_WWS_FAMILY" };
        m[TT_NAME_ID_WWS_SUBFAMILY] = NameID{ TT_NAME_ID_WWS_SUBFAMILY,"TT_NAME_ID_WWS_SUBFAMILY" };
    
        /* This is new in OpenType 1.7 */
        m[TT_NAME_ID_LIGHT_BACKGROUND] = NameID{ TT_NAME_ID_LIGHT_BACKGROUND,"TT_NAME_ID_LIGHT_BACKGROUND" };
        m[TT_NAME_ID_DARK_BACKGROUND] = NameID{ TT_NAME_ID_DARK_BACKGROUND,"TT_NAME_ID_DARK_BACKGROUND" };
    
        /* This is new in OpenType 1.8 */
        m[TT_NAME_ID_VARIATIONS_PREFIX] = NameID{ TT_NAME_ID_VARIATIONS_PREFIX,"TT_NAME_ID_VARIATIONS_PREFIX" };
    
        /* these two values are deprecated */
        m[TT_NAME_ID_PREFERRED_FAMILY] = NameID{ TT_NAME_ID_PREFERRED_FAMILY,"TT_NAME_ID_PREFERRED_FAMILY" };
        m[TT_NAME_ID_PREFERRED_SUBFAMILY] = NameID{ TT_NAME_ID_PREFERRED_SUBFAMILY,"TT_NAME_ID_PREFERRED_SUBFAMILY" };
    }
    
    
    
    
    //TT_PLATFORM_APPLE_UNICODE
    void create_encoding_id_apple(
        std::unordered_map<unsigned int, EncodingID_Apple>& m) {
    
        m[TT_APPLE_ID_DEFAULT] = EncodingID_Apple{ TT_APPLE_ID_DEFAULT         ,"TT_APPLE_ID_DEFAULT" };
        m[TT_APPLE_ID_UNICODE_1_1] = EncodingID_Apple{ TT_APPLE_ID_UNICODE_1_1     ,"TT_APPLE_ID_UNICODE_1_1" };
        m[TT_APPLE_ID_ISO_10646] = EncodingID_Apple{ TT_APPLE_ID_ISO_10646       ,"TT_APPLE_ID_ISO_10646" };
        m[TT_APPLE_ID_UNICODE_2_0] = EncodingID_Apple{ TT_APPLE_ID_UNICODE_2_0     ,"TT_APPLE_ID_UNICODE_2_0" };
        m[TT_APPLE_ID_UNICODE_32] = EncodingID_Apple{ TT_APPLE_ID_UNICODE_32      ,"TT_APPLE_ID_UNICODE_32" };
        m[TT_APPLE_ID_VARIANT_SELECTOR] = EncodingID_Apple{ TT_APPLE_ID_VARIANT_SELECTOR,"TT_APPLE_ID_VARIANT_SELECTOR" };
        m[TT_APPLE_ID_FULL_UNICODE] = EncodingID_Apple{ TT_APPLE_ID_FULL_UNICODE    ,"TT_APPLE_ID_FULL_UNICODE" };
    
    }
    
    
    // TT_PLATFORM_MACINTOSH
    void create_encoding_id_MAC(
        std::unordered_map<unsigned int, EncodingID_MACINTOSH>& m
    ) {
        m[TT_MAC_ID_ROMAN] = EncodingID_MACINTOSH{ TT_MAC_ID_ROMAN               ,"TT_MAC_ID_ROMAN" };
        m[TT_MAC_ID_JAPANESE] = EncodingID_MACINTOSH{ TT_MAC_ID_JAPANESE            ,"TT_MAC_ID_JAPANESE" };
        m[TT_MAC_ID_TRADITIONAL_CHINESE] = EncodingID_MACINTOSH{ TT_MAC_ID_TRADITIONAL_CHINESE ,"TT_MAC_ID_TRADITIONAL_CHINESE" };
        m[TT_MAC_ID_KOREAN] = EncodingID_MACINTOSH{ TT_MAC_ID_KOREAN              ,"TT_MAC_ID_KOREAN" };
        m[TT_MAC_ID_ARABIC] = EncodingID_MACINTOSH{ TT_MAC_ID_ARABIC              ,"TT_MAC_ID_ARABIC" };
        m[TT_MAC_ID_HEBREW] = EncodingID_MACINTOSH{ TT_MAC_ID_HEBREW              ,"TT_MAC_ID_HEBREW" };
        m[TT_MAC_ID_GREEK] = EncodingID_MACINTOSH{ TT_MAC_ID_GREEK               ,"TT_MAC_ID_GREEK" };
        m[TT_MAC_ID_RUSSIAN] = EncodingID_MACINTOSH{ TT_MAC_ID_RUSSIAN             ,"TT_MAC_ID_RUSSIAN" };
        m[TT_MAC_ID_RSYMBOL] = EncodingID_MACINTOSH{ TT_MAC_ID_RSYMBOL             ,"TT_MAC_ID_RSYMBOL" };
        m[TT_MAC_ID_DEVANAGARI] = EncodingID_MACINTOSH{ TT_MAC_ID_DEVANAGARI          ,"TT_MAC_ID_DEVANAGARI" };
        m[TT_MAC_ID_GURMUKHI] = EncodingID_MACINTOSH{ TT_MAC_ID_GURMUKHI            ,"TT_MAC_ID_GURMUKHI" };
        m[TT_MAC_ID_GUJARATI] = EncodingID_MACINTOSH{ TT_MAC_ID_GUJARATI            ,"TT_MAC_ID_GUJARATI" };
        m[TT_MAC_ID_ORIYA] = EncodingID_MACINTOSH{ TT_MAC_ID_ORIYA               ,"TT_MAC_ID_ORIYA" };
        m[TT_MAC_ID_BENGALI] = EncodingID_MACINTOSH{ TT_MAC_ID_BENGALI             ,"TT_MAC_ID_BENGALI" };
        m[TT_MAC_ID_TAMIL] = EncodingID_MACINTOSH{ TT_MAC_ID_TAMIL               ,"TT_MAC_ID_TAMIL" };
        m[TT_MAC_ID_TELUGU] = EncodingID_MACINTOSH{ TT_MAC_ID_TELUGU              ,"TT_MAC_ID_TELUGU" };
        m[TT_MAC_ID_KANNADA] = EncodingID_MACINTOSH{ TT_MAC_ID_KANNADA             ,"TT_MAC_ID_KANNADA" };
        m[TT_MAC_ID_MALAYALAM] = EncodingID_MACINTOSH{ TT_MAC_ID_MALAYALAM           ,"TT_MAC_ID_MALAYALAM" };
        m[TT_MAC_ID_SINHALESE] = EncodingID_MACINTOSH{ TT_MAC_ID_SINHALESE           ,"TT_MAC_ID_SINHALESE" };
        m[TT_MAC_ID_BURMESE] = EncodingID_MACINTOSH{ TT_MAC_ID_BURMESE             ,"TT_MAC_ID_BURMESE" };
        m[TT_MAC_ID_KHMER] = EncodingID_MACINTOSH{ TT_MAC_ID_KHMER               ,"TT_MAC_ID_KHMER" };
        m[TT_MAC_ID_THAI] = EncodingID_MACINTOSH{ TT_MAC_ID_THAI                ,"TT_MAC_ID_THAI" };
        m[TT_MAC_ID_LAOTIAN] = EncodingID_MACINTOSH{ TT_MAC_ID_LAOTIAN             ,"TT_MAC_ID_LAOTIAN" };
        m[TT_MAC_ID_GEORGIAN] = EncodingID_MACINTOSH{ TT_MAC_ID_GEORGIAN            ,"TT_MAC_ID_GEORGIAN" };
        m[TT_MAC_ID_ARMENIAN] = EncodingID_MACINTOSH{ TT_MAC_ID_ARMENIAN            ,"TT_MAC_ID_ARMENIAN" };
        m[TT_MAC_ID_MALDIVIAN] = EncodingID_MACINTOSH{ TT_MAC_ID_MALDIVIAN           ,"TT_MAC_ID_MALDIVIAN" };
        m[TT_MAC_ID_SIMPLIFIED_CHINESE] = EncodingID_MACINTOSH{ TT_MAC_ID_SIMPLIFIED_CHINESE  ,"TT_MAC_ID_SIMPLIFIED_CHINESE" };
        m[TT_MAC_ID_TIBETAN] = EncodingID_MACINTOSH{ TT_MAC_ID_TIBETAN             ,"TT_MAC_ID_TIBETAN" };
        m[TT_MAC_ID_MONGOLIAN] = EncodingID_MACINTOSH{ TT_MAC_ID_MONGOLIAN           ,"TT_MAC_ID_MONGOLIAN" };
        m[TT_MAC_ID_GEEZ] = EncodingID_MACINTOSH{ TT_MAC_ID_GEEZ                ,"TT_MAC_ID_GEEZ" };
        m[TT_MAC_ID_SLAVIC] = EncodingID_MACINTOSH{ TT_MAC_ID_SLAVIC              ,"TT_MAC_ID_SLAVIC" };
        m[TT_MAC_ID_VIETNAMESE] = EncodingID_MACINTOSH{ TT_MAC_ID_VIETNAMESE          ,"TT_MAC_ID_VIETNAMESE" };
        m[TT_MAC_ID_SINDHI] = EncodingID_MACINTOSH{ TT_MAC_ID_SINDHI              ,"TT_MAC_ID_SINDHI" };
        m[TT_MAC_ID_UNINTERP] = EncodingID_MACINTOSH{ TT_MAC_ID_UNINTERP            ,"TT_MAC_ID_UNINTERP" };
    }
    
    
    // TT_PLATFORM_ISO
    void create_encoding_id_iso(
        std::unordered_map<unsigned int, EncodingID_ISO>& m
    ) {
        m[TT_ISO_ID_7BIT_ASCII] = EncodingID_ISO{ TT_ISO_ID_7BIT_ASCII,"TT_ISO_ID_7BIT_ASCII" };
        m[TT_ISO_ID_10646] = EncodingID_ISO{ TT_ISO_ID_10646     ,"TT_ISO_ID_10646" };
        m[TT_ISO_ID_8859_1] = EncodingID_ISO{ TT_ISO_ID_8859_1    ,"TT_ISO_ID_8859_1" };
    }
    
    
    //TT_PLATFORM_MICROSOFT
    void create_encoding_id_Microsoft(
        std::unordered_map<unsigned int, EncodingID_Microsoft>& m
    ) {
        m[TT_MS_ID_SYMBOL_CS] = EncodingID_Microsoft{ TT_MS_ID_SYMBOL_CS ,"TT_MS_ID_SYMBOL_CS" };
        m[TT_MS_ID_UNICODE_CS] = EncodingID_Microsoft{ TT_MS_ID_UNICODE_CS,"TT_MS_ID_UNICODE_CS" };
        m[TT_MS_ID_SJIS] = EncodingID_Microsoft{ TT_MS_ID_SJIS      ,"TT_MS_ID_SJIS" };
        m[TT_MS_ID_PRC] = EncodingID_Microsoft{ TT_MS_ID_PRC       ,"TT_MS_ID_PRC" };
        m[TT_MS_ID_BIG_5] = EncodingID_Microsoft{ TT_MS_ID_BIG_5     ,"TT_MS_ID_BIG_5" };
        m[TT_MS_ID_WANSUNG] = EncodingID_Microsoft{ TT_MS_ID_WANSUNG   ,"TT_MS_ID_WANSUNG" };
        m[TT_MS_ID_JOHAB] = EncodingID_Microsoft{ TT_MS_ID_JOHAB     ,"TT_MS_ID_JOHAB" };
        m[TT_MS_ID_UCS_4] = EncodingID_Microsoft{ TT_MS_ID_UCS_4     ,"TT_MS_ID_UCS_4" };
        m[TT_MS_ID_GB2312] = EncodingID_Microsoft{ TT_MS_ID_GB2312    ,"TT_MS_ID_GB2312" };
    }
    
    
    //TT_PLATFORM_ADOBE
    void create_encoding_id_Adobe(
        std::unordered_map<unsigned int, EncodingID_Adobe>& m
    ) {
        m[TT_ADOBE_ID_STANDARD] = EncodingID_Adobe{ TT_ADOBE_ID_STANDARD,"TT_ADOBE_ID_STANDARD" };
        m[TT_ADOBE_ID_EXPERT] = EncodingID_Adobe{ TT_ADOBE_ID_EXPERT  ,"TT_ADOBE_ID_EXPERT  " };
        m[TT_ADOBE_ID_CUSTOM] = EncodingID_Adobe{ TT_ADOBE_ID_CUSTOM  ,"TT_ADOBE_ID_CUSTOM  " };
        m[TT_ADOBE_ID_LATIN_1] = EncodingID_Adobe{ TT_ADOBE_ID_LATIN_1 ,"TT_ADOBE_ID_LATIN_1 " };
    }
    

    FreeType2でフォントファイルのSFNT Nameを読んでフォント名などを取り出す(1)

    例えば "meiryo.ttc"から「メイリオ」、"msmincho.ttc"から「MS 明朝」などを取得する。

    フォントファイル(ttfやttcなど)の中にはsfntテーブルというのがあり、そこにコピーライト、バージョン、フォントファミリ名などが格納されている。

    具体的に情報が入っているのはFT_SfntName::string という変数(文字列)なので、これを取り出すことを考える。

    方法

    1.データの取得

    sfntテーブルには項目がたくさんあるので、FT_Get_Sfnt_NameCountで項目数を取得してからFT_Get_Sfnt_Nameでi番目の情報を取得する。

      // テーブル内の項目数を取得
      FT_Int count = FT_Get_Sfnt_Name_Count(face);
    
      // 取得した項目をここへ格納
      FT_SfntName sfnt_name;
    
      // すべての項目を読み込む
      for (FT_Int i = 0; i < count; i++) {
    
          // 取得
          FT_Get_Sfnt_Name(face, i, &sfnt_name);

    // sfnt_nameを処理


     }

    2.データの種類の特定

    上記、sfnt_name変数にデータをとれたので、その情報が何を表しているか(コピーライトかフォント名かなど)を特定する。

    platform_id

    まず、プラットフォームという括りでデータが分けられている。

    プラットフォーム==Microsoft  , フォント名 == メイリオ

    プラットフォーム==MAC , フォント名==メイリオ

    プラットフォーム==Apple ,  フォント名==メイリオ

    みたいな構造をしている。なのですべてのプラットフォームを見てしまうと情報が重複するので、例えばMicrosoftだけに絞って見たほうがいい。

    プラットフォームの種類はマクロで定義されているので、その一覧でswitchする。

    プラットフォームの定義: https://freetype.org/freetype2/docs/reference/ft2-truetype_tables.html#tt_platform_xxx

        // 取得
        FT_Get_Sfnt_Name(face, i, &sfnt_name);
    
    
        switch (sfnt_name.platform_id) {
        case  TT_PLATFORM_APPLE_UNICODE:
            // 処理
            break;
        case  TT_PLATFORM_MACINTOSH::
            // 処理
            break;
        case  TT_PLATFORM_ISO::
            // 処理
            break;
        case  TT_PLATFORM_MICROSOFT::
            // 処理
            break;
        case  TT_PLATFORM_CUSTOM::
            // 処理
            break;
        case  TT_PLATFORM_ADOBE::
            // 処理
            break;
        }
    

    name_id

    name_idには情報の種類が定義されている。これで、上記sfnt_nameに入っているデータがフォント名かコピーライトかの区別ができる。

    name_idの定義:https://freetype.org/freetype2/docs/reference/ft2-truetype_tables.html#tt_name_id_xxx

      FT_Get_Sfnt_Name(face, i, &sfnt_name);
    
    
      switch (sfnt_name.platform_id) {
      case  TT_PLATFORM_MICROSOFT:
          switch (sfnt_name.name_id) {
          case TT_NAME_ID_FULL_NAME:
              printf("これはフォント名\n");
              break;
          case TT_NAME_ID_VERSION_STRING:
              printf("これはバージョン\n");
              break;
          case TT_NAME_ID_COPYRIGHT:
              printf("これは著作権表記\n");
              break;
          }
          break;
    
      default:
          // 今はMicrosoft以外のプラットフォームは考えない
          break;
      }
    

    このプログラムをmeiryo.ttcに対して走らせると、以下のように表示される。

    これは著作権表記
    これはフォント名
    これはバージョン
    これは著作権表記
    これはフォント名
    これはバージョン

    なぜ各項目が二つもあるのか。これはその情報が文字列で格納されているのだが、その言語ごとに入っているからだ。

    language_id

    最終的に求めるのが FT_SfntName::string という文字列なのだが、ここに何語で格納されているかという情報がlanguage_idに入っている。そこで言語による切り替えをしてみる。

    なおlanguage_idの定義はplatform_idによって分かれている。

    platform_idがTT_PLATFORM_MACINTOSHの場合

    https://freetype.org/freetype2/docs/reference/ft2-truetype_tables.html#tt_mac_langid_xxx

    platform_idがTT_PLATFORM_MICROSOFTの場合

    https://freetype.org/freetype2/docs/reference/ft2-truetype_tables.html#tt_ms_langid_xxx

    FT_Get_Sfnt_Name(face, i, &sfnt_name);
    
    bool target = true;
    switch (sfnt_name.platform_id) {
    case  TT_PLATFORM_MICROSOFT:
      switch (sfnt_name.name_id) {
      case TT_NAME_ID_FULL_NAME:
        printf("これはフォント名  ");
        break;
      case TT_NAME_ID_VERSION_STRING:
        printf("これはバージョン  ");
        break;
      case TT_NAME_ID_COPYRIGHT:
        printf("これは著作権表記  ");
        break;
      default:
        target = false;
        break;
      }
    
      if (target == true) {
    
    
        FT_UShort langID = sfnt_name.language_id;
        switch (langID) {
        case TT_MS_LANGID_ENGLISH_UNITED_STATES:
          printf("TT_MS_LANGID_ENGLISH_UNITED_STATES (0x%04x)", langID);
          break;
        case TT_MS_LANGID_JAPANESE_JAPAN:
          printf("TT_MS_LANGID_JAPANESE_JAPAN (0x%04x)", langID);
          break;
        }
        puts("");
      }
    
    
      break;
    
    
    default:
      // 今はMicrosoft以外のプラットフォームは考えない
      break;
    }
    

    このプログラムをmeiryo.ttcに対して走らせると、以下のように表示される。

    これは著作権表記 TT_MS_LANGID_ENGLISH_UNITED_STATES (0x0409)
    これはフォント名 TT_MS_LANGID_ENGLISH_UNITED_STATES (0x0409)
    これはバージョン TT_MS_LANGID_ENGLISH_UNITED_STATES (0x0409)
    これは著作権表記 TT_MS_LANGID_JAPANESE_JAPAN (0x0411)
    これはフォント名 TT_MS_LANGID_JAPANESE_JAPAN (0x0411)
    これはバージョン TT_MS_LANGID_JAPANESE_JAPAN (0x0411)

    encoding_id

    FT_SfntName::string は言語ごとに入っているといった。これが「同じ項目が複数ある理由」である。

    language_idはデータを整理するうえでは重要だが、データを人間が読める形にするためにはあまり意味をなさない。データを実際に取り出すには、encoding_idで分岐する必要がある。

    今回は諸事情によりmeiryo.ttcを使うがこの中にはエンコードが一つしかない。

    あと、エンコードの一覧もplatform_idごとに分かれている。

    TT_PLATFORM_APPLE_UNICODE
    https://freetype.org/freetype2/docs/reference/ft2-truetype_tables.html#tt_apple_id_xxx

    TT_PLATFORM_MACINTOSH
    https://freetype.org/freetype2/docs/reference/ft2-truetype_tables.html#tt_mac_id_xxx

    TT_PLATFORM_ISO
    https://freetype.org/freetype2/docs/reference/ft2-truetype_tables.html#tt_iso_id_xxx

    TT_PLATFORM_MICROSOFT
    https://freetype.org/freetype2/docs/reference/ft2-truetype_tables.html#tt_ms_id_xxx

    TT_PLATFORM_ADOBE
    https://freetype.org/freetype2/docs/reference/ft2-truetype_tables.html#tt_adobe_id_xxx

      FT_Get_Sfnt_Name(face, i, &sfnt_name);
    
      bool target = true;
      switch (sfnt_name.platform_id) {
      case  TT_PLATFORM_MICROSOFT:
        switch (sfnt_name.name_id) {
        case TT_NAME_ID_FULL_NAME:printf("これはフォント名  ");break;
        case TT_NAME_ID_VERSION_STRING:printf("これはバージョン  ");break;
        case TT_NAME_ID_COPYRIGHT:printf("これは著作権表記  ");break;
        default:
          //それ以外の項目は考えない
          target = false;
          break;
        }
    
        if (target == true) {
    
          FT_UShort langID = sfnt_name.language_id;
          switch (langID) {
          case TT_MS_LANGID_ENGLISH_UNITED_STATES:printf("(英語)");break;
          case TT_MS_LANGID_JAPANESE_JAPAN:printf("(日本語)");break;
          default:
            //それ以外の言語は考えない
            break;
          }
    
    
          FT_UShort encID = sfnt_name.encoding_id;
          switch (encID) {
          case TT_MS_ID_UNICODE_CS:
            printf(" TT_MS_ID_UNICODE_CS (%d)", encID);
            break;
          default:
            //それ以外のエンコードは考えない
            break;
          }
          puts("");
        }
    
    
        break;
    
    
      default:
        // 今はMicrosoft以外のプラットフォームは考えない
        break;
      }
    

    このプログラムをmeiryo.ttcに対して走らせると、以下のように表示される。

    これは著作権表記 (英語) TT_MS_ID_UNICODE_CS (1)
    これはフォント名 (英語) TT_MS_ID_UNICODE_CS (1)
    これはバージョン (英語) TT_MS_ID_UNICODE_CS (1)
    これは著作権表記 (日本語) TT_MS_ID_UNICODE_CS (1)
    これはフォント名 (日本語) TT_MS_ID_UNICODE_CS (1)
    これはバージョン (日本語) TT_MS_ID_UNICODE_CS (1)

    string, string_len

    ようやくデータを取り出せる。

    FT_SfntName::string にはフォント名などが文字列で入っている

    FT_SfntName::string_len にはFT_SfntName::stringのサイズがバイト数で入っている。

    注意点は、encoding_idがTT_MS_ID_UNICODE_CSの場合、UTF16BEで入っていてwindowsだとそのまま表示できないのでLEに変換してやる必要がある。

    FT_UShort encID = sfnt_name.encoding_id;
    switch (encID) {
    case TT_MS_ID_UNICODE_CS:
    
    {
      std::wstring wstr;
      for (size_t i = 0; i < sfnt_name.string_len; i += 2) {
        char16_t w = *((char16_t*)&sfnt_name.string[i]);
        // UTF16BEなので2バイトを反転する
        unsigned char* p = (unsigned char*)&w;
        std::swap(p[0], p[1]);
    
        wstr += w;
      }
      printf(" %ls", wstr.c_str());
    }
    
    これは著作権表記 (英語) c 2019 Microsoft Corporation. All Rights Reserved.
    これはフォント名 (英語) Meiryo
    これはバージョン (英語) Version 6.50
    これは著作権表記 (日本語) c 2019 Microsoft Corporation. All Rights Reserved.
    これはフォント名 (日本語) メイリオ
    これはバージョン (日本語) Version 6.50

    参考:

    https://freetype.org/freetype2/docs/reference/ft2-sfnt_names.html

    所感

    疲れた。

    icuライブラリで書記素を扱うラッパーを作ってみた

    GTextの使用例

    #include <iostream>
    
    // 自作ラッパー
    #include "GraphemeText.hpp"
    
    #pragma warning(disable:4996)
    
    int main()
    {
      // 文字列の定義
      GText gtext(u"aあغ山👨‍👧經经");
    
      gtext.insert(0, u"غ");
    
      FILE* fp = fopen("C:\\test\\test.txt", "wb");
    
      for (size_t i = 0; i < gtext.length(); i++) {
    
        wchar_t c[100]{ 0 };
        int len = gtext[i].size();
        
        // 一文字書き込み
        memcpy(c, gtext[i].ptr(), len);
        fwrite(c, 1, len, fp);
      }
    
      fclose(fp);
    
    }
    

    GraphemeText.hpp

    #pragma once
    
    // 以下のdllを要求される
    // icudt69.dll
    // icuuc69.dll
    
    
    //! @brief 書記素クラス
    class Grapheme {
      void* _UnicodeString;
      int _index;
      int _length;
      enum UScriptCode _script;
      bool _emoji;
    public:
    
      Grapheme(void* _UnicodeString_, int _index_, int _length_);
      ~Grapheme() {}
    
      const char16_t* ptr()const;
    
      //! @brief この書記素の元の文字列上のindexを取得。
      int index()const { return _index; }
    
      //! @brief この書記素のchar16_tとしての文字数。
      int length()const {return _length;}
    
      //! @brief この書記素のバイト数。
      int size()const { return _length * 2; }
    
      //! @brief この文字の用字の種類
      enum UScriptCode script()const { return _script; }
    
      //! @brief これが絵文字かどうか
      bool isEmoji()const { return _emoji; }
    };

    ///////////////////////////////////
    //! @brief 書記素の配列オブジェクト class GText { struct GText_Impl* _impl; public: GText(const char16_t* text); GText(const GText& src); ~GText(); const Grapheme& operator[](const size_t index)const; //! @brief この文字列の書記素数 size_t length()const; //! @brief 書記素を一つ置き換える //! @attention Graphemeリストを更新するのでこれまでのGraphemeは使えなくなる void replace(const size_t index, const char16_t c); void replace(const size_t index, const char16_t* c); void erase(const size_t index); void insert(const size_t index, const char16_t c); void insert(const size_t index, const char16_t* c); const char16_t* ptr()const; //! @brief この文字列のバイト数 int size()const; };

    GraphemeText.cpp

    #include<vector>
    #include <string>
    
    #include <unicode/ucnv.h>
    #include <unicode/brkiter.h>
    #include <unicode/utypes.h>
    #include <unicode/uscript.h>
    
    #include "GraphemeText.hpp"
    
    
    // 要リンク
    #pragma comment(lib, "icuuc.lib")
    #pragma comment(lib, "icudt.lib")
    
    // 書記素クラス
    Grapheme::Grapheme(void* _UnicodeString_, int _index_, int _length_) :
      _UnicodeString(_UnicodeString_),
      _index(_index_),
      _length(_length_)
    {
    
      icu::UnicodeString* p = (icu::UnicodeString*)_UnicodeString;
    
      UErrorCode err;
    
      UChar32 c32 = p->char32At(_index);
    
      // スクリプト
      _script = uscript_getScript(c32, &err);
    
      _emoji = false;
      // 絵文字かどうかのフラグをセット
    
      _emoji |= (bool)u_getIntPropertyValue(c32, UCHAR_EMOJI);
      _emoji |= (bool)u_getIntPropertyValue(c32, UCHAR_EMOJI_PRESENTATION);
      _emoji |= (bool)u_getIntPropertyValue(c32, UCHAR_EMOJI_MODIFIER);
      _emoji |= (bool)u_getIntPropertyValue(c32, UCHAR_EMOJI_MODIFIER_BASE);
      _emoji |= (bool)u_getIntPropertyValue(c32, UCHAR_EMOJI_COMPONENT);
    
    
    }
    
    
    const char16_t* Grapheme::ptr()const {
      return static_cast<icu::UnicodeString*>(_UnicodeString)->getBuffer() + _index;
    }
    
    
    
    // Graphemeの配列を作成
    void
    makeGraphemeList(std::vector< Grapheme >& glist, icu::UnicodeString& text) { UErrorCode err; icu::BreakIterator* bi = icu::BreakIterator::createCharacterInstance( icu::Locale::getDefault(), err); if (bi == nullptr) throw "bi is NULL"; bi->setText(text); int32_t current = bi->first(); while (current != icu::BreakIterator::DONE) { int32_t prev = current; current = bi->next(); if (current == UBRK_DONE) { break; } int32_t count = current - prev;//文字の長さ //書記素保存 glist.emplace_back(&text, prev, count); } delete bi; }
    
          
    struct GText_Impl {
      std::vector< Grapheme > glist;
      icu::UnicodeString utext;
    };
    
    GText::GText(const char16_t* text) {
      _impl = new GText_Impl;
    
      _impl->utext = text;
    
      makeGraphemeList(_impl->glist, _impl->utext);
    }
    
    const char16_t* GText::ptr()const {
      return _impl->utext.getBuffer();
    }
    
    
    GText::GText(const GText& src) {
      _impl->utext = src._impl->utext;
    
      _impl->glist.clear();
    
      makeGraphemeList(_impl->glist, _impl->utext);
    
    }
    
    GText::~GText() {
      delete _impl;
    }
    
    const Grapheme& GText::operator[](const size_t index)const {
      return _impl->glist[index];
    }
    
    size_t GText::length()const {
      return _impl->glist.size();
    }
    int GText::size()const {
      return _impl->utext.length() * 2;
    }
    
    void GText::replace(const size_t index, const char16_t c) {
    
      if (index >= _impl->glist.size())
        throw "GText replace index is too big";
    
      _impl->utext.replace(_impl->glist[index].index(), _impl->glist[index].length(), c);
      _impl->glist.clear();
      makeGraphemeList(_impl->glist, _impl->utext);
    
    
    }
    
    void GText::replace(const size_t index, const char16_t* c) {
      if (index >= _impl->glist.size())
        throw "GText replace index is too big";
    
      size_t len = std::char_traits<char16_t>::length(c);
    
      _impl->utext.replace(_impl->glist[index].index(), _impl->glist[index].length(),c, len);
      _impl->glist.clear();
      makeGraphemeList(_impl->glist, _impl->utext);
    
    }
    
    
    void GText::erase(const size_t index) {
      if (index >= _impl->glist.size())
        throw "GText erase index is too big";
    
      _impl->utext.remove(_impl->glist[index].index(), _impl->glist[index].length());
    
      _impl->glist.clear();
      makeGraphemeList(_impl->glist, _impl->utext);
    }
    
    void GText::insert(const size_t index, const char16_t c) {
      if (index >= _impl->glist.size())
        throw "GText insert index is too big";
    
      _impl->utext.insert(_impl->glist[index].index(), c);
    
      _impl->glist.clear();
      makeGraphemeList(_impl->glist, _impl->utext);
    
    }
    
    void GText::insert(const size_t index, const char16_t* c) {
      if (index >= _impl->glist.size())
        throw "GText insert index is too big";
    
      size_t len = std::char_traits<char16_t>::length(c);
    
      _impl->utext.insert(_impl->glist[index].index(), c, len);
      _impl->glist.clear();
      makeGraphemeList(_impl->glist, _impl->utext);
    
    }
    

    std::listを使っていてlist iterators incompatibleが出た。

    以下のプログラムをデバッグモードで(←重要)走らせたところエラーが発生。

    #include <iostream>
    
    #include <list>
    
    int main()
    {
    
      std::list<int> aa,bb;
      aa.push_back(0);
      bb.push_back(1);
    
      // 違うリストのイテレータを比較
      if (aa.begin() == bb.begin())
        std::cout << "same" << std::endl;
    
    }
    
    Expression: list iterators incompatible

    原因と王道的な回避策

    このエラーは、

    ・違うリストのイテレータ同士を比較し

    かつ

    ・_ITERATOR_DEBUG_LEVEL==2の時

    のとき起こる。だからデバッグーモードでは起こるがリリースモードでは起こらないということがありうる。

    このエラーが出ている個所のコードを抜粋すると以下のようになっている。

      _NODISCARD bool operator==(const _List_const_iterator& _Right) const noexcept {
    #if _ITERATOR_DEBUG_LEVEL == 2
        _STL_VERIFY(this->_Getcont() == _Right._Getcont(), "list iterators incompatible");
    #endif // _ITERATOR_DEBUG_LEVEL == 2
    
        return this->_Ptr == _Right._Ptr;
      }
    

    つまりイテレータのoperator==()の中でイテレータの正当性をチェックし、意味のない比較が行われたときは意図してエラーを出している。

    従って、

    /D"_ITERATOR_DEBUG_LEVEL=0"

    などを指定してやれば回避できる。

     

    ほかの解決方法を考える

    この部分はイテレータの正当性をチェックし意図的に発生させているエラーなので、自前で書いてしまえばoperator==の前に検出できる。

    #include <iostream>
    
    #include <list>
    
    int main()
    {
    
      std::list<int> aa,bb;
      aa.push_back(0);
      bb.push_back(1);
    
      std::list<int>::iterator i1 = aa.begin();
      std::list<int>::iterator i2 = bb.begin();
    
    #if _ITERATOR_DEBUG_LEVEL==2
    
      // デバッグ時、異なるリスト同士の比較が生じる前にチェックする
      if (i1._Getcont() != i2._Getcont()) {
        std::cout << " i1 != i2 " << std::endl;
      }
      else 
    #endif
    
      {
        // デバッグ時、異なるリスト同士の比較が生じることはない
        if (i1 == i2) {
          std::cout << "i1 == i2" << std::endl;
        }
        else {
          std::cout << "i1 != i2" << std::endl;
        }
      }
    }
    

    ICUライブラリのicu::UnicodeStringを前方宣言したらC2757が発生

    経緯

    icuライブラリのラッパーを作成したくなり、hppにicu::UnicodeStringのポインタを持たせ、cpp内でnewする構造にした。この時、前方宣言したらエラーC2757が発生。原因はicuという名前空間が実は存在していないかららしい。

    まず、エラーの出るコードは以下。

    エラーの出るコード

    icuwrap.hpp

    #pragma once
    
    // クラスが名前空間に入っている時は前方宣言をこうする
    namespace icu{ // C2757
      class UnicodeString;
    }
    
    // icu::UnicodeStringのラッパー
    struct MyICU {
      icu::UnicodeString* ustr;
    };
    
    // ICUライブラリのUnicodeStringのラッパーを作成
    MyICU make_my_icu(const char16_t* text);
    

    icuwrap.cpp

    #include <unicode/ucnv.h>
    #include <unicode/brkiter.h>
    
    #include"icuwrap.h"
    
    // 要リンク
    #pragma comment(lib, "icuuc.lib")
    #pragma comment(lib, "icudt.lib")
    
    
    MyICU make_my_icu(const char16_t* text) {
      MyICU tmp;
    
      tmp.ustr = new icu::UnicodeString(text);
    
      return tmp;
    }
    

    main.cpp

    #include "icuwrap.h"
    
    int main()
    {
      MyICU micu = make_my_icu(u"hello");
    
    }
    

    すると以下のコンパイルエラーが発生する

    error C2757: 'icu': この名前のシンボルは既に存在します。この名前を名前空間名として使用することはできません。

    原因

    構造

    icuというリテラルは unicode/uversion.h の中で定義されているのだが、実はこれはU_ICU_NAMESPACEマクロで生成されたもののエイリアスで、さらにこれはU_ICU_ENTRY_POINT_RENAMEマクロで作成されている

    unicode/uversion.h    Line 105~

    #       define U_ICU_NAMESPACE U_ICU_ENTRY_POINT_RENAME(icu)
            namespace U_ICU_NAMESPACE { }
            namespace icu = U_ICU_NAMESPACE;
    

    ではこの U_ICU_ENTRY_POINT_RENAME マクロの定義はというと、こちらは unicode/uvernum.h で定義されている。

    unicode/uversion.h    Line 128~

    #       define U_DEF_ICU_ENTRY_POINT_RENAME(x,y) x ## y
    #       define U_DEF2_ICU_ENTRY_POINT_RENAME(x,y) U_DEF_ICU_ENTRY_POINT_RENAME(x,y)
    #       define U_ICU_ENTRY_POINT_RENAME(x)    U_DEF2_ICU_ENTRY_POINT_RENAME(x,U_ICU_VERSION_SUFFIX)
    

    ここで ## はトークン結合演算子で、x は 「icu」、U_ICU_VERSION_SUFFIXは「_69」など、_ + ライブラリのバージョンが入っている

    unicode/uversion.h    Line 89

    #define U_ICU_VERSION_SUFFIX _69
    

    つまり「icu」は、「icu_69」のエイリアスである。

    つまり

    前方宣言で自分が定義した「namespace icu」というモノが既にあるのに、同じ「icu」をicu_69のエイリアスとして使おうとしたことになって、エラーが起こっている。

    エラーの回避策

    バージョンが固定でいいなら以下のように正規の名前空間で前方宣言すれば解決する

    // クラスが名前空間に入っている時は前方宣言をこうする
    namespace icu_69{
      class UnicodeString;
    }
    
    struct MyICU {
      icu_69::UnicodeString* ustr;
    };
    
    // ICUライブラリのUnicodeStringのラッパーを作成
    MyICU make_my_icu(const char16_t* text);
    

    しかしどうせ隠すなら素直にpimplとして完全に隠す方がいい。

    余談

    U_DISABLE_RENAMINGマクロを使えば解決しそうに見えるが、マニュアルには「内部処理用だから使うな」と書いてあるのであきらめる。

    OpenGL(GLSL)でfloat型のテクスチャを使う

    rgbaがfloat[4]のテクスチャを使う。

     

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, TEXWIDTH, TEXHEIGHT, 0,GL_RGBA, GL_FLOAT, texdata);

    GL_RGBA32F,GL_RGBA,GL_FLOATを指定する。他はunsigned charの時と変わらない。

    サンプル

    #pragma comment(lib,"glew32s.lib")
    
    #define GLEW_STATIC
    #include <gl/glew.h>
    
    #include <GL/glut.h>
    
    
    GLuint ProgramID;
    GLuint FragmentShaderID;
    GLuint VertexShaderID;
    
    typedef GLfloat rgba_t[4];  //テクスチャ用
    
    typedef GLfloat points_t[3]; //モデルの座標用
    typedef GLfloat texcoord_t[2];//テクスチャ座標用
    
    const int P_COUNT = 4;
    
    //頂点データ
    GLuint vertexbuffer;
    points_t position[P_COUNT];
    
    //テクスチャ座標データ
    texcoord_t texcoord[P_COUNT];
    GLuint texcoordbuffer;
    
    //テクスチャデータ
    rgba_t texdata[P_COUNT];
    GLuint textureID;
    
    void prepare_buffers() {
    
      //テクスチャ(画像)作成
      // 2*2の画像
      texdata[0][0] = 1.0;
      texdata[0][1] = 0;
      texdata[0][2] = 0;
      texdata[0][2] = 0;
    
      texdata[1][0] = 0;
      texdata[1][1] = 1.0;
      texdata[1][2] = 0;
      texdata[0][2] = 0;
      texdata[0][2] = 0;
    
      texdata[2][0] = 0;
      texdata[2][1] = 0;
      texdata[2][2] = 1.0;
      texdata[0][2] = 0;
    
      texdata[3][0] = 1.0;
      texdata[3][1] = 1.0;
      texdata[3][2] = 1.0;
      texdata[0][2] = 0;
    
      //////////////////////////////////////////
      //////////////////////////////////////////
    
      //頂点座標の定義 (四角形)
      // いつもなら glVertex3fv等て指定するもの
      position[0][0] = -0.7;
      position[0][1] = 0.7;
      position[0][2] = 0;
    
      position[1][0] = -0.7;
      position[1][1] = -0.7;
      position[1][2] = 0;
    
      position[2][0] = 0.7;
      position[2][1] = -0.7;
      position[2][2] = 0;
    
      position[3][0] = +0.7;
      position[3][1] = +0.7;
      position[3][2] = 0;
    
      //////////////////////////////////////////
      //////////////////////////////////////////
    
      //テクスチャ座標の定義
      //いつもならglTexCoord2f等で指定するもの
      texcoord[0][0] = 0;
      texcoord[0][1] = 1;
    
      texcoord[1][0] = 0;
      texcoord[1][1] = 0;
    
      texcoord[2][0] = 1;
      texcoord[2][1] = 0;
    
      texcoord[3][0] = 1;
      texcoord[3][1] = 1;
    
      //////////////////////////////////////////
      //////////////////////////////////////////
    
      //テクスチャの作成
      // 普通のテクスチャ作成
      glGenTextures(1, &textureID);
      glBindTexture(GL_TEXTURE_2D, textureID);
    
      int TEXWIDTH = 2;  // 2*2の画素数
      int TEXHEIGHT = 2;
    
      glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    
      // テクスチャの割り当て
      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, TEXWIDTH, TEXHEIGHT, 0,GL_RGBA, GL_FLOAT, texdata);
    
      // テクスチャを拡大・縮小する方法の指定
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    
      //////////////////////////////////////////
      //////////////////////////////////////////
    
      //頂点バッファの作成
      glGenBuffers(1, &vertexbuffer);
      glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
      glBufferData(GL_ARRAY_BUFFER, 3 * P_COUNT * sizeof(GLfloat), position, GL_STATIC_DRAW);
    
      //テクスチャ座標バッファの作成
      glGenBuffers(1, &texcoordbuffer);
      glBindBuffer(GL_ARRAY_BUFFER, texcoordbuffer);
      glBufferData(GL_ARRAY_BUFFER, 2 * P_COUNT * sizeof(GLfloat), texcoord, GL_STATIC_DRAW);
    
    }
    
    void display(void)
    {
      glClearColor(0, 0, 0, 1);
      glClear(GL_COLOR_BUFFER_BIT);
      glDisable(GL_CULL_FACE);//カリングを無効にする
    
    
      ///////////////////////////////////
      // 行列の設定
      glMatrixMode(GL_PROJECTION);
      glLoadIdentity();
      gluPerspective(65, 1, 0.1, 10);
    
      glMatrixMode(GL_MODELVIEW);
      glLoadIdentity();
      glTranslated(0.0, 0.0, -3);
    
      ///////////////////////////////////
      // シェーダを使う
      glUseProgram(ProgramID);
    
      ///////////////////////////////////
      // 頂点バッファを有効化
      glEnableVertexAttribArray(0);
      glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
      glVertexAttribPointer(
        0,          // 属性0
        3,          // 1要素の個数。GLfloatのx,y,zなので3
        GL_FLOAT,   // タイプ
        GL_FALSE,   // 正規化しない(データが整数型の時)
        0,          // ストライド
        (void*)0    // 配列バッファオフセット
      );
    
      ///////////////////////////////////
      //テクスチャ座標バッファの有効化
      glEnableVertexAttribArray(1);
      glBindBuffer(GL_ARRAY_BUFFER, texcoordbuffer);
      glVertexAttribPointer(
        1,          // 属性1
        2,          // 1要素の個数。GLfloatのu,vなので2
        GL_FLOAT,   // タイプ
        GL_FALSE,   // 正規化しない(データが整数型の時)
        0,          // ストライド
        (void*)0    // 配列バッファオフセット
      );
    
    
      ///////////////////////////////////
      // 四角形の描画
      glDrawArrays(GL_TRIANGLE_FAN, 0, P_COUNT);
    
      glDisableVertexAttribArray(0);
      glDisableVertexAttribArray(1);
    
      glFlush();
    }
    

    Assimpのテスト

    非常に多くのモデルデータを読み込めるBSDライセンスのライブラリ。

    ビルドはそんなに難しくないのでとりあえずサンプルコードを先に置いておく。

     

    元のサンプルコード

    http://assimp.sourceforge.net/lib_html/usage.html

    Bunnyのデータ

    https://commons.wikimedia.org/wiki/File:Stanford_Bunny.stl

    なおstanford bunnyは(スケールが)大きいのでgluLookatで少し遠いところにカメラを設置している。

    #include <iostream>
    #include <vector>
    #include <array>
    
    
    ////////////////////////////
    // 表示用のOpenGL
    
    // NOMINMAXをしておかないとmaterial.inlでstd::minでエラーが起こる
    #define NOMINMAX
    #include <Windows.h>
    
    #include <gl/GL.h>
    #include <gl/GLU.h>
    #include <gl/freeglut.h>
    
    ////////////////////////////
    // Assimp
    #include <assimp/Importer.hpp>    // C++ importer interface
    #include <assimp/scene.h>       // Output data structure
    #include <assimp/postprocess.h>   // Post processing flags
    
    #pragma comment(lib,"assimp-vc142-mt.lib")
    ////////////////////////////
    
    //読み込んだポリゴンデータの格納先
    std::vector<std::array<unsigned int, 3>> triangles;
    std::vector<std::array<float, 3>> points;
    
    ////////////////////////////
    
    
    //! @brief メッシュファイルを読み込み
    //! @sa http://assimp.sourceforge.net/lib_html/usage.html
    bool MyDataImporter(
      std::vector<std::array<unsigned int, 3>>& faces,
      std::vector<std::array<float, 3>>& coords,
      const std::string& pFile)
    {
      // Create an instance of the Importer class
      Assimp::Importer importer;
    
      const aiScene* scene = importer.ReadFile(pFile,
        aiProcess_CalcTangentSpace |  //接ベクトル空間を計算する
        aiProcess_Triangulate |     //全ての面を三角形分割する
        aiProcess_JoinIdenticalVertices //重複頂点をマージする
      );
    
      // If the import failed, report it
      if (!scene)
      {
        printf("失敗:%s\n", importer.GetErrorString());
        return false;
      }
    
      if (scene->HasMeshes()) {
    
        //メッシュの配列
        aiMesh** p = scene->mMeshes;
    
        //最初のメッシュへアクセス
        aiMesh* mesh0 = p[0];
    
    //三角形一覧取得 int face_count = mesh0->mNumFaces; for (size_t findex = 0; findex < face_count; findex++) { aiFace& face = mesh0->mFaces[findex]; if (face.mNumIndices == 3) { faces.push_back( std::array<unsigned int, 3>{ face.mIndices[0], face.mIndices[1], face.mIndices[2] } ); } }
    //頂点一覧取得 int vertex_count = mesh0->mNumVertices;
    for (size_t vindex = 0; vindex < vertex_count; vindex++) {
    aiVector3D& vtx = mesh0->mVertices[vindex]; coords.push_back( std::array<float, 3>{vtx.x, vtx.y, vtx.z} );
    } } // 終了(解放不要) return true; }
    //ウィンドウの幅と高さ
    int width, height;
    
    //描画関数
    void disp(void) {
    
      glViewport(0, 0, width, height);
    
      glClearColor(0.2, 0.2, 0.2, 1);
      glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
      glMatrixMode(GL_PROJECTION);
      glLoadIdentity();
      gluPerspective(45, 1, 0.1, 500);
      glMatrixMode(GL_MODELVIEW);
      glLoadIdentity();
      gluLookAt(
        100, 200, 100, 
        0, 0, 50, 
        0, 0, 1);
    
    
      glEnable(GL_CULL_FACE);
      double v = 0.7;
      glColor3d(1, 0, 0);
      for (size_t f = 0; f < triangles.size(); f++) {
        int p0 = triangles[f][0];
        int p1 = triangles[f][1];
        int p2 = triangles[f][2];
    
        glBegin(GL_LINE_LOOP);
        glVertex3fv(points[p0].data());
        glVertex3fv(points[p1].data());
        glVertex3fv(points[p2].data());
        glEnd();
    
    
      }
    
    
    
      glEnd();
      glFlush();
    }
    
    //ウィンドウサイズの変化時に呼び出される
    void reshape(int w, int h) {
      width = w; height = h;
    
      disp();
    }
    
    //エントリポイント
    int main(int argc, char** argv)
    {
      glutInit(&argc, argv);
      glutInitWindowPosition(100, 50);
      glutInitWindowSize(500, 500);
      glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);
    
      glutCreateWindow("sample");
      glutDisplayFunc(disp);
      glutReshapeFunc(reshape);
    
    
    
    
      std::string f = R"(D:\dev\Stanford_Bunny.stl)";
    
      MyDataImporter(triangles, points, f);
    
    
      glutMainLoop();
    
      return 0;
    }
    

    実行結果