ぬの部屋(仮)
nu-no-he-ya
  •     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
           
  • 色々な型に対応できる関数を作る(1)構造体対応

    1.基本

    以下のようなコードがある。vec_tは三次元のベクトル型で、lengthはその長さを計算する

    len.hpp

    #pragma once
    
    
    struct vec_t { double x, y, z; };
    double length(const vec_t& v) { return sqrt( v.x * v.x + v.y * v.y + v.z * v.z ); }

    source.cpp

    #include <iostream>
    
    #include "len.hpp"
    
    int main()
    {
      vec_t vv;
      vv.x = 1;
      vv.y = 1;
      vv.z = 1;
    
      double dbl = length(vv);
    
      std::cout << dbl << std::endl;
    
      int i;
      std::cin >>i;
    
    }
    

    lengthはシンプルで可読性が高いが、移植性には欠ける。

    具体的にはvec_tにしか対応していない。(故に読みやすいのだが。)

    2.テンプレート化

    length関数をテンプレート化する

    len.hpp

    #pragma once
    
    
    struct vec_t { double x, y, z; };
    template<typename T> double length(const T& v) { return sqrt( v.x * v.x + v.y * v.y + v.z * v.z ); }

    source.cpp

    これでx,y,zというメンバ変数がpublicにあればコンパイルできる。

    #include <iostream>
    
    #include "len.hpp"
    
    
    struct xyz_t { double x, y, z; };
    int main() { double L; vec_t vv; vv.x = 1; vv.y = 1; vv.z = 1; L = length(vv); std::cout << L << std::endl; xyz_t xyz; xyz.x = 1; xyz.y = 1; xyz.z = 1; L = length(xyz); std::cout << L << std::endl; int i; std::cin >>i; }

    ではx,y,zがないときはどうすればいいのか。

    3.テンプレートでデータ取得用の関数を指定する

    len.hpp

    #pragma once
    
    
    //デフォルトの型 struct vec_t { double x, y, z; };
    //プライマリテンプレート template<typename T> struct GETTER;
    //特殊化1(デフォルトの型) template<> struct GETTER<vec_t> { using arg_t = vec_t; using carg_t = const vec_t; static inline double& X(arg_t& v) { return v.x; } static inline double& Y(arg_t& v) { return v.y; } static inline double& Z(arg_t& v) { return v.z; } static inline const double& X(carg_t& v) { return v.x; } static inline const double& Y(carg_t& v) { return v.y; } static inline const double& Z(carg_t& v) { return v.z; } };

    template< typename T, class G = GETTER<T> > double length(const T & v) { return sqrt( G::X(v) * G::X(v) + G::Y(v) * G::Y(v) + G::Z(v) * G::Z(v) ); }

    source.hpp

    #include <iostream>
    
    #include "len.hpp"
    
    //自作の型
    struct XYZ_t {
      double X, Y, Z;
    };
     
    //特殊化2(自作の型用) template<> struct GETTER<XYZ_t> { using arg_t = XYZ_t; using carg_t = const XYZ_t; static inline double& X(arg_t& v) { return v.X; } static inline double& Y(arg_t& v) { return v.Y; } static inline double& Z(arg_t& v) { return v.Z; } static inline const double& X(carg_t& v) { return v.X; } static inline const double& Y(carg_t& v) { return v.Y; } static inline const double& Z(carg_t& v) { return v.Z; } };
    //エントリポイント int main() { double L; vec_t vv; vv.x = 1; vv.y = 1; vv.z = 1; L = length(vv); std::cout << L << std::endl; XYZ_t xyz; xyz.X = 1; xyz.X = 1; xyz.X = 1; L = length(xyz); std::cout << L << std::endl; int i; std::cin >> i; }

    これでGETTERを定義すればどんな型でも使える。

    続く...

    C++の64bit版のhash_combine

    Boostのhash_combineの64bit版

    https://github.com/HowardHinnant/hash_append/issues/7

    最新のC++にはBoostから正式に仕様に含められたhashが「unordered_map」という名前で入っているが、hash_combineは取り入れられていないらしい。なので自分で作る必要がある。

    この時、多くはBoostを参考にすると思うのだが、Boostの中のマジックナンバーがどうやら32bitのもので、64bit版の場合、この数字はどうなるんだ?というのが上記サイトの議論の内容となっている。

    そもそもこの数字はTEAアルゴリズムというものが根拠らしく、64bitの場合と、さらに128bitの場合の値が上げられている。

    ビット数16進数表記の値
    80x9e
    160x9e37
    320x9e3779b9
    640x9e3779b97f4a7c15
    1280x9e3779b97f4a7c15f39cc0605d396154

    そして64bitは32bitの二倍なので、シフト量も二倍にする。

    seed ^= std::hash<T>{}(val) + 0x9e3779b9U + (seed<<6) + (seed>>2);

    seed ^= std::hash<T>{}(val) + 0x9e3779b97f4a7c15LLU + (seed<<12) + (seed>>4);

    なおこの(Boostの)HashCombineはさして優れているわけでは無いと言うことで、別の方法があるならそれを使うべきと言うような主張をちらほら見かける。

    CityHashからの借用

    https://stackoverflow.com/questions/8513911/how-to-create-a-good-hash-combine-with-64-bit-output-inspired-by-boosthash-co

    CityHashはGoogleが考案したハッシュアルゴリズムで、MITライセンス(らしい)。そこから転用したコードが紹介されている。

    template <class T> inline void hash_combine(std::size_t& seed, const T& v)
    {
        std::hash<T> hasher;
        const std::size_t kMul = 0x9ddfea08eb382d69ULL;
        std::size_t a = (hasher(v) ^ seed) * kMul;
        a ^= (a >> 47);
        std::size_t b = (seed ^ a) * kMul;
        b ^= (b >> 47);
        seed = b * kMul;
    }
    

    (このスニペットは、CityHashコードの「実質的な部分」を構成するものではないため、ここや他の場所に複製しても問題ないと思いますが、CityHashのソースとライセンス契約を確認して、自分で決めてください)

    (Google翻訳)

    CMake中に出てきた大量のC2220への対処

    c2220 警告をエラーとして扱いました。'object' ファイルは生成されません。

    Open3DのCMakeを試していたのだけれど、VC++2017でC2220が大量発生した。関連するプロジェクトが無数にある場合で、一つ一つプロパティを開くのは避けたい場合、王道な方法が見つからなかったのでCMakeList.txtを編集して対処した。

    プロジェクトが少ない場合

    単体のプロジェクトなら、以下のように /WX オプションを解除することで対処できる。

    プロジェクトが複数の場合

    CMakeLists.txtをテキストエディタで開き、中の「/WX」という部分を削除する。

    set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /MP ${MSVC_WARNING_IGNORE} /WX")
    set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /MP ${MSVC_WARNING_IGNORE} /WX")
    set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MP ${MSVC_WARNING_IGNORE} /WX")
    set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MP ${MSVC_WARNING_IGNORE} /WX")

    glewを使用しないでC++からGLSLを使用

    glewのライセンス

    https://github.com/nigels-com/glew#copyright-and-licensing

    GLEW is originally derived from the EXTGL project by Lev Povalahev. The source code is licensed under the Modified BSD License, the Mesa 3-D License (MIT) and the Khronos License (MIT).

    The automatic code generation scripts are released under the GNU GPL.

    glewはBSD LicenseかMITライセンス。普通誰も気にしないが、気に入らない場合は使用できない。

    けれどやっていることはヘッダファイル側で定数を#defineしているのと、dll側でwglGetProcAddressしているだけ(多分)なので、必要な部分だけ自前で書くことも不可能ではない。

    myglinit.hpp

    #pragma once
    
    #include <gl/GL.h>
    
    typedef ptrdiff_t GLsizeiptr;
    typedef char GLchar;
    
    //サンプルのコンパイルに必要なものだけを取り出したもの
    
    
    // 関数ポインタ型
    using PFNGLGENBUFFERSPROC               = void(_stdcall*) (GLsizei n, GLuint* buffers);
    using PFNGLBINDBUFFERPROC               = void(_stdcall*) (GLenum target, GLuint buffer);
    using PFNGLBUFFERDATAPROC               = void(_stdcall*) (GLenum target, GLsizeiptr size, const void* data, GLenum usage);
    using PFNGLCREATESHADERPROC             = GLuint(_stdcall*) (GLenum type);
    using PFNGLSHADERSOURCEPROC             = void(_stdcall*) (GLuint shader, GLsizei count, const GLchar* const* string, const GLint* length);
    using PFNGLCOMPILESHADERPROC            = void(_stdcall*) (GLuint shader);
    using PFNGLGETSHADERIVPROC              = void(_stdcall*) (GLuint shader, GLenum pname, GLint* param);
    using PFNGLGETSHADERINFOLOGPROC         = void(_stdcall*) (GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* infoLog);
    using PFNGLCREATEPROGRAMPROC            = GLuint(_stdcall*) (void);
    using PFNGLATTACHSHADERPROC             = void(_stdcall*) (GLuint program, GLuint shader);
    using PFNGLLINKPROGRAMPROC              = void(_stdcall*) (GLuint program);
    using PFNGLGETPROGRAMIVPROC             = void(_stdcall*) (GLuint program, GLenum pname, GLint* param);
    using PFNGLGETPROGRAMINFOLOGPROC        = void(_stdcall*) (GLuint program, GLsizei bufSize, GLsizei* length, GLchar* infoLog);
    using PFNGLUSEPROGRAMPROC               = void(_stdcall*) (GLuint program);
    using PFNGLENABLEVERTEXATTRIBARRAYPROC  = void(_stdcall*) (GLuint index);
    using PFNGLVERTEXATTRIBPOINTERPROC      = void(_stdcall*) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* pointer);
    using PFNGLDISABLEVERTEXATTRIBARRAYPROC = void(_stdcall*) (GLuint index);
    
    // 定数
    #define GL_ARRAY_BUFFER    0x8892
    #define GL_STATIC_DRAW     0x88E4
    #define GL_VERTEX_SHADER   0x8B31
    #define GL_COMPILE_STATUS  0x8B81
    #define GL_INFO_LOG_LENGTH 0x8B84
    #define GL_FRAGMENT_SHADER 0x8B30
    #define GL_LINK_STATUS     0x8B82
    
    // wglGetProcAddressで取得したアドレスを格納する変数(グローバル)
    PFNGLGENBUFFERSPROC               glGenBuffers;
    PFNGLBINDBUFFERPROC               glBindBuffer;
    PFNGLBUFFERDATAPROC               glBufferData;
    PFNGLCREATESHADERPROC             glCreateShader;
    PFNGLSHADERSOURCEPROC             glShaderSource;
    PFNGLCOMPILESHADERPROC            glCompileShader;
    PFNGLGETSHADERIVPROC              glGetShaderiv;
    PFNGLGETSHADERINFOLOGPROC         glGetShaderInfoLog;
    PFNGLCREATEPROGRAMPROC            glCreateProgram;
    PFNGLATTACHSHADERPROC             glAttachShader;
    PFNGLLINKPROGRAMPROC              glLinkProgram;
    PFNGLGETPROGRAMIVPROC             glGetProgramiv;
    PFNGLGETPROGRAMINFOLOGPROC        glGetProgramInfoLog;
    PFNGLUSEPROGRAMPROC               glUseProgram;
    PFNGLENABLEVERTEXATTRIBARRAYPROC  glEnableVertexAttribArray;
    PFNGLVERTEXATTRIBPOINTERPROC      glVertexAttribPointer;
    PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray;
    
    
    //関数のアドレスを取得
    inline void myglinit() {
    
      glAttachShader             = (PFNGLATTACHSHADERPROC)wglGetProcAddress("glAttachShader");
      glBindBuffer               = (PFNGLBINDBUFFERPROC)wglGetProcAddress("glBindBuffer");
      glBufferData               = (PFNGLBUFFERDATAPROC)wglGetProcAddress("glBufferData");
      glCompileShader            = (PFNGLCOMPILESHADERPROC)wglGetProcAddress("glCompileShader");
      glCreateProgram            = (PFNGLCREATEPROGRAMPROC)wglGetProcAddress("glCreateProgram");
      glCreateShader             = (PFNGLCREATESHADERPROC)wglGetProcAddress("glCreateShader");
      glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC)wglGetProcAddress("glDisableVertexAttribArray");
      glEnableVertexAttribArray  = (PFNGLENABLEVERTEXATTRIBARRAYPROC)wglGetProcAddress("glEnableVertexAttribArray");
      glGenBuffers               = (PFNGLGENBUFFERSPROC)wglGetProcAddress("glGenBuffers");
      glGetProgramInfoLog        = (PFNGLGETPROGRAMINFOLOGPROC)wglGetProcAddress("glGetProgramInfoLog");
      glGetProgramiv             = (PFNGLGETPROGRAMIVPROC)wglGetProcAddress("glGetProgramiv");
      glGetShaderInfoLog         = (PFNGLGETSHADERINFOLOGPROC)wglGetProcAddress("glGetShaderInfoLog");
      glGetShaderiv              = (PFNGLGETSHADERIVPROC)wglGetProcAddress("glGetShaderiv");
      glLinkProgram              = (PFNGLLINKPROGRAMPROC)wglGetProcAddress("glLinkProgram");
      glShaderSource             = (PFNGLSHADERSOURCEPROC)wglGetProcAddress("glShaderSource");
      glUseProgram               = (PFNGLUSEPROGRAMPROC)wglGetProcAddress("glUseProgram");
      glVertexAttribPointer      = (PFNGLVERTEXATTRIBPOINTERPROC)wglGetProcAddress("glVertexAttribPointer");
    
    }
    

    サンプルコード(本体)

    元のソースコード

    #include <iostream>
    
    #pragma warning(disable:4996)
    
    //#pragma comment(lib,"glew32.lib")  --- しない
    
    
    //#include <gl/glew.h>   --- しない
    #include <Windows.h>
    #include <GL/glut.h>
    
    #include <fstream>
    #include <sstream>
    #include <vector>
    #include <algorithm>
    
    #include "myglinit.hpp"
    
    
    void init(void);
    void display(void);
    
    void link_program();
    void prepare_buffers();
    void prepare_vertex_shader();
    void prepare_fragment_shader();
    
    
    
    int main(int argc, char *argv[])
    {
      glutInit(&argc, argv);
      glutInitDisplayMode(GLUT_RGBA);
      glutCreateWindow(argv[0]);
      glutDisplayFunc(display);
      //■ glewInit(); // glewの初期化 --- しない
      myglinit();// glewInit()関数の代わり
      init();
      glutMainLoop();
      return 0;
    }
    
    void init(void)
    {
    
      //頂点データと色情報の作成
      prepare_buffers();
    
      //頂点シェーダの準備
      prepare_vertex_shader();
    
      //フラグメントシェーダの準備
      prepare_fragment_shader();
    
      //プログラムのリンク
      link_program();
    
    }
    
    //バッファとデータ
    typedef GLfloat points_t[3];
    GLuint vertexbuffer;//バッファのIDを格納する変数
    GLuint colorbuffer;//バッファのIDを格納する変数
    points_t position[3];
    points_t color[3];
    
    //////////////////////////////////////////////////
    void prepare_buffers() {
    
      //頂点座標
      position[0][0] = 0;
      position[0][1] = 0.5;
      position[0][2] = 0;
    
      position[1][0] = 0.5;
      position[1][1] = -0.5;
      position[1][2] = 0;
    
      position[2][0] = -0.5;
      position[2][1] = -0.5;
      position[2][2] = 0;
    
    
      //色
      color[0][0] = 1.0;
      color[0][1] = 0.0;
      color[0][2] = 0.0;
    
      color[1][0] = 0.0;
      color[1][1] = 1.0;
      color[1][2] = 0.0;
    
      color[2][0] = 0.0;
      color[2][1] = 0.0;
      color[2][2] = 1.0;
    
      glGenBuffers(1, &vertexbuffer);
      glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
      glBufferData(
        GL_ARRAY_BUFFER,
        3 * 3 * sizeof(GLfloat),
        position,
        GL_STATIC_DRAW);
    
      glGenBuffers(1, &colorbuffer);
      glBindBuffer(GL_ARRAY_BUFFER, colorbuffer);
      glBufferData(
        GL_ARRAY_BUFFER,
        3 * 3 * sizeof(GLfloat),
        color,
        GL_STATIC_DRAW);
    
    }
    
    GLuint VertexShaderID;
    
    void prepare_vertex_shader() {
    
      //////////////////////////////////////////////
        // シェーダを作ります
        // (作るといっても宣言みたいなもの)
      VertexShaderID = glCreateShader(GL_VERTEX_SHADER);
    
      ////////////////////////////////////////////
      // ファイルから頂点シェーダを読み込みます。
      // 注意 ここはSTLのifstreamとかstringstreamの使い方の話で、
      // OpenGL命令は一つも無い。
      const char * vertex_file_path = ".\\verts.sh";
      std::string VertexShaderCode;
      std::ifstream VertexShaderStream(vertex_file_path, std::ios::in);
      if (VertexShaderStream.is_open())
      {
        std::stringstream sstr;
        sstr << VertexShaderStream.rdbuf();
        VertexShaderCode = sstr.str();
        VertexShaderStream.close();
      }
      ////////////////////////////////////////////
      // 頂点シェーダをコンパイルします。
      printf("Compiling shader : %s\n", vertex_file_path);
      char const * VertexSourcePointer = VertexShaderCode.c_str();
      glShaderSource(VertexShaderID, 1, &VertexSourcePointer, NULL);
      glCompileShader(VertexShaderID);
    
      ////////////////////////////////////////////
      // エラーチェック
      GLint Result = GL_FALSE;
      int InfoLogLength;
    
      // 頂点シェーダをチェックします。
      glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result);
      glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
      if (Result == FALSE) {
        std::vector<char> VertexShaderErrorMessage(InfoLogLength);
        glGetShaderInfoLog(VertexShaderID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]);
        fprintf(stdout, "%s\n", &VertexShaderErrorMessage[0]);
      }
    
    }
    
    GLuint FragmentShaderID;
    
    void prepare_fragment_shader() {
    
      /////////////////////////////////////////////
      // シェーダを作ります。
      FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
    
      /////////////////////////////////////////////
      // ファイルからフラグメントシェーダを読み込みます。
      const char * fragment_file_path = ".\\frags.sh";
      std::string FragmentShaderCode;
      std::ifstream FragmentShaderStream(fragment_file_path, std::ios::in);
      if (FragmentShaderStream.is_open()) {
        std::stringstream sstr;
        sstr << FragmentShaderStream.rdbuf();
        FragmentShaderCode = sstr.str();
        FragmentShaderStream.close();
      }
    
      /////////////////////////////////////////////
      // フラグメントシェーダをコンパイルします。
      printf("Compiling shader : %s\n", fragment_file_path);
      char const * FragmentSourcePointer = FragmentShaderCode.c_str();
      glShaderSource(FragmentShaderID, 1, &FragmentSourcePointer, NULL);
      glCompileShader(FragmentShaderID);
    
      GLint Result = GL_FALSE;
      int InfoLogLength;
    
      /////////////////////////////////////////////
      // フラグメントシェーダをチェックします。
      glGetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &Result);
      glGetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
      if (Result == GL_FALSE) {
        std::vector<char> FragmentShaderErrorMessage(InfoLogLength);
        glGetShaderInfoLog(FragmentShaderID, InfoLogLength, NULL, &FragmentShaderErrorMessage[0]);
        fprintf(stdout, "%s\n", &FragmentShaderErrorMessage[0]);
      }
    }
    
    GLuint ProgramID;
    
    void link_program() {
    
      GLint Result = GL_FALSE;
      int InfoLogLength;
    
      ////////////////////////////////////////
      // プログラムをリンクします。
      fprintf(stdout, "Linking program\n");
      ProgramID = glCreateProgram();
      glAttachShader(ProgramID, VertexShaderID);
      glAttachShader(ProgramID, FragmentShaderID);
      glLinkProgram(ProgramID);
    
      ////////////////////////////////////////
      // プログラムをチェックします。
      glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result);
      glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength);
      std::vector<char> ProgramErrorMessage((std::max)(InfoLogLength, int(1)));
      glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]);
      fprintf(stdout, "%s\n", &ProgramErrorMessage[0]);
    
    }
    
    void display(void)
    {
      glClearColor(0, 0, 0, 1);
      glClear(GL_COLOR_BUFFER_BIT);
    
      glLoadIdentity();
      glTranslated(0.5, 0.0, 0.0);//平行移動
    
      // シェーダを使う
      glUseProgram(ProgramID);
    
      // 頂点バッファ:頂点
      glEnableVertexAttribArray(0);
      glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
      glVertexAttribPointer(
        0,                  // シェーダ内のlayoutとあわせる
        3,                  // 1要素の要素数(x,y,z)で3要素
        GL_FLOAT,           // タイプ
        GL_FALSE,           // 正規化しない(データが整数型の時)
        0,                  // ストライド
        (void*)0            // 配列バッファオフセット
      );
    
      // カラーバッファを有効にする
      glEnableVertexAttribArray(1);
      glBindBuffer(GL_ARRAY_BUFFER, colorbuffer);
      glVertexAttribPointer(
        1,                  // シェーダ内のlayoutとあわせる
        3,                  // 1要素の要素数(r,g,b)で3要素
        GL_FLOAT,           // タイプ
        GL_FALSE,           // 正規化しない(データが整数型の時)
        0,                  // ストライド
        (void*)0            // 配列バッファオフセット
      );
    
      // 三角形を描きます!
      glDrawArrays(GL_TRIANGLES, 0, 3);
    
      glDisableVertexAttribArray(0);//バッファを無効にする
      glDisableVertexAttribArray(1);
    
      glFlush();
    }
    

    後書き

    他にもリンクしちゃいけないときとかもこの方法をとることになると思う。ただその場合はglext.hを使えば自分で#define やtypedefを並べたりしなくて良くなる・・・のか?

    「MIT Licenseを気にする状況ってどんな状況?」とか言う人がいそうなのだけれど、個人でならともかく業務でやっていると、自分より上の層で「俺が理解できないものは低品質なガラクタ」とか「俺の知らないものは使えないゴミ」等の謎理論で意味不明な利用制限がかかったりするので、できるかできないかぐらいは把握しておいた方がいい。後は、そういう契約になってるんだ!とか後から言われたり。しないかな?しないよね。

    C/C++で負数の右論理シフト(>>)をする

    概要

    ・ビットシフト(>>)には算術シフトと論理シフトがある。

    ・算術シフトは負数をシフトすると左側が1で埋まる (Arithmetic shift)

    ・論理シフトは負数をシフトすると左側が0で埋まる (Logical shift)

    ・どちらの挙動をするかは処理系依存

    ということで、強制的に論理シフトしたい時は工夫する必要がある。

    結論

    unsignedにキャストする。以下C++のコード。

    具体例1

    #include <iostream>
    
    //表示のためにbitsetを使う
    #include <bitset>
    
    int main()
    {
      char val = -40;
    
      char S = val;
      char A = val >> 2;//処理系依存 VC++2019では算術シフト
      char L = static_cast<unsigned char>(val) >> 2;//論理シフト
    
      std::bitset<8> Sdisp(S);
      std::bitset<8> Adisp(A);
      std::bitset<8> Ldisp(L);
      std::cout << "source                   : " << Sdisp << " == " << static_cast<int>(S) << '\n';
      std::cout << "not cast         and >>2 : " << Adisp << " == " << static_cast<int>(A) << '\n';
      std::cout << "cast to unsigned and >>2 : " << Ldisp << " == " << static_cast<int>(L) << '\n';
    
      getchar();
    
      return 0;
    }
    

    具体例2(テンプレート)

    同じ事を関数化するなら以下のような感じになる。

    #include <iostream>
    #include<type_traits>
    
    //! @brief 論理右シフト
    //! @param [in] val 右シフトする値
    //! @param [in] shift 右シフトするビット数
    //! @return val >> shift
    template<typename T>
    inline auto logical_right_shift(const T val,const int shift) {
      return
        static_cast<
          typename std::make_unsigned<T>::type>(val) >> shift;
    }
    
    int main()
    {
    
      char val = -40;
    
      char S = val;
      char A = val >> 2;//処理系依存 VC++2019では算術シフト
      char L = logical_right_shift(val, 2);//論理シフト
    
      std::bitset<8> Sdisp(S);
      std::bitset<8> Adisp(A);
      std::bitset<8> Ldisp(L);
      std::cout << "source                    : " << Sdisp << " == " << static_cast<int>(S) << '\n';
      std::cout << "not cast          and >>2 : " << Adisp << " == " << static_cast<int>(A) << '\n';
      std::cout << "logicalrightshift 2       : " << Ldisp << " == " << static_cast<int>(L) << '\n';
    
    
      getchar();
    }
    

    あとがき

    負数の算術シフトが処理系依存とは。流石Cと言わざるを得ない。

    Blender 2.8 PythonでNURBS Curveを追加

    ソースコード

    import bpy
    from mathutils import Vector
     
    surface_data = bpy.data.curves.new('wook', 'SURFACE')
    surface_data.dimensions = '3D'
     
    # 16 coordinates, set points per segments (U * V)
    points = [
        Vector((-1.5, -1.5, 0.0, 1.0)), Vector((-1.5, -0.5, 0.0, 1.0)), #    0,   0,   1 /   0,   1,   0
        Vector((-1.5, 0.5, 0.0, 1.0)), Vector((-1.5, 1.5, 0.0, 1.0)),   #    0,   1,   1 /   1,   0,   0
        Vector((-0.5, -1.5, 0.0, 1.0)), Vector((-0.5, -0.5, 1.0, 1.0)), #    1,   0,   1 /   1,   1,   0
        Vector((-0.5, 0.5, 1.0, 1.0)), Vector((-0.5, 1.5, 0.0, 1.0)),   #    1,   1,   1 /   0,   0,   0
        Vector((0.5, -1.5, 0.0, 1.0)), Vector((0.5, -0.5, 1.0, 1.0)),   #  0.0, 0,0, 0.5 / 0.0, 0,5, 0.0
        Vector((0.5, 0.5, 1.0, 1.0)), Vector((0.5, 1.5, 0.0, 1.0)),     #  0.0, 0.5, 0.5 / 0.5, 0.0, 0.0
        Vector((1.5, -1.5, 0.0, 1.0)), Vector((1.5, -0.5, 0.0, 1.0)),   #  0.5, 0.0, 0.5 / 0.5, 0.5, 0.0
        Vector((1.5, 0.5, 0.0, 1.0)), Vector((1.5, 1.5, 0.0, 1.0))      #  0.5, 0.5, 0.5 / 1.0, 0.2, 0.2
    ]
    
    for i in range(0, 16, 4):
        spline = surface_data.splines.new(type='NURBS')
        spline.points.add(3)  # already has a default zero vector
     
        for p, new_co in zip(spline.points, points[i:i+4]):
            p.co = new_co
     
    surface_object = bpy.data.objects.new('NURBS_OBJ', surface_data)
    
    
    splines = surface_object.data.splines
    for s in splines:
        for p in s.points:
            p.select = True
    
    #######################################
    #######################################
    # 新しいCollrectionを作成
    newCol = bpy.data.collections.new('Collection 1')
    
    # 現在のシーンにコレクションをリンク
    bpy.context.scene.collection.children.link(newCol)
    
    # コレクションにオブジェクトをリンク
    newCol.objects.link(surface_object)
    #######################################
    #######################################
    
    bpy.context.view_layer.objects.active = surface_object
    bpy.ops.object.mode_set(mode='EDIT')
    bpy.ops.curve.make_segment()
     
     
    
    #https://blender.stackexchange.com/questions/126577/blender-2-8-api-python-set-active-object
    

    頂点の対応

    参考

    https://blender.stackexchange.com/questions/126577/blender-2-8-api-python-set-active-object

    Blender 2.8 Pythonからbezier curveを追加

    コード

    import bpy
    
    
    
    curvedata = bpy.data.curves.new("my curve", type='CURVE')    
    curvedata.dimensions = '3D'    
    
    polyline = curvedata.splines.new('BEZIER')
    polyline.bezier_points.add(2) # 制御点数ー1個
    
    polyline.bezier_points[0].co = 0,0,0
    polyline.bezier_points[0].handle_left = -1,0,0
    polyline.bezier_points[0].handle_right = 1,0,0
    
    polyline.bezier_points[1].co = 2,0,1
    polyline.bezier_points[1].handle_left = 1,0,1
    polyline.bezier_points[1].handle_right = 3,0,1
    
    polyline.bezier_points[2].co = 4,0,0
    polyline.bezier_points[2].handle_left = 3,0,0
    polyline.bezier_points[2].handle_right = 5,0,0
    
    
    
    
    obj = bpy.data.objects.new("my bezier", curvedata) 
    
    
    # 新しいCollrectionを作成
    newCol = bpy.data.collections.new('Collection 1')
    
    # 現在のシーンにコレクションをリンク
    bpy.context.scene.collection.children.link(newCol)
    
    # コレクションにオブジェクトをリンク
    newCol.objects.link(obj)
    

    実行結果

    Blender 2.8 Pythonでメタボールを追加

    コード

    import bpy
    
    #############################################################
    #############################################################
    # メタボール(一つ目)
    
    
    
    # add metaball object
    mball = bpy.data.metaballs.new("MyBall")
    mball.resolution = 0.15  # View resolution
    #mball.render_resolution
    
    ele = mball.elements.new()
    ele.co = (0.0, 0.0, 0.0)
    ele.use_negative = False
    ele.radius = 2.0
    
    # create meta ball
    obj1 = bpy.data.objects.new("MyBallObjectK", mball) 
    
    
    #############################################################
    #############################################################
    # メタボール(二つ目)
    
    
    mball = bpy.data.metaballs.new("MyBall")
    mball.resolution = 0.15  # View resolution
    
    ele = mball.elements.new()
    ele.co = (1.0, 1.0, 1.0)
    ele.use_negative = False
    ele.radius = 2.0
    
    # create meta ball
    obj2 = bpy.data.objects.new("MyBallObjectK", mball)# 名前を同じにすることで MyBallObjectK.001 のような名前にする
    
    
    
    #############################################################
    #############################################################
    
    
    # 新しいCollrectionを作成
    newCol = bpy.data.collections.new('Collection 1')
    
    # 現在のシーンにコレクションをリンク
    bpy.context.scene.collection.children.link(newCol)
    
    newCol.objects.link(obj1)
    newCol.objects.link(obj2)
    
    #https://developer.blender.org/T33455
    

    Blender メタボールのオブジェクトの名前について

    現象

    Blender Pythonでメタボールを作ろうとして、以下の問題に遭遇した。

    つまり、メタボールのオブジェクトを二つ作ったが中身が表示されなかった。

    理想は左
    メタボールの中身が表示されない(右)

    理由

    メタボールの挙動は、オブジェクト名に依存する。

    メタボールは、オブジェクト名が数字で終わらないものをmotherとし、それ以外の、同じ名前 + . + 数字のオブジェクト同士が結合する

    ・Kball / Mball ... 名前が違うと独立する

    ・Mball2 ... 名前の最後が数字だと表示されない

    ・Kball / Kball2  ... 名前が同じでも、単純に後ろに数字を付けるだけでは表示されない

    ・Kball / Kball.001 ... 同じ名前.数字 の組み合わせで初めてくっつく

    bit depth が非8の倍数ビットの時に画素値を取得する

    libpngで8bit,16bit以外のビット深度のグレイスケール画像を読み込むと、メモリ上には1bit,2bit,4bitなどごとにデータが格納されているので、1byteごとデータを取り出すような簡単な処理で取得できない。

    ちょっと調べた限りでは画素値を素直に取り出す関数などが見当たらなかった(あるかもしれない)のでとりあえず適当に作ることにした。

    動作

    例えば、bit depth==4で

    10101011 , 01001110 , 10000101 というデータがあったら、

    1010,1011,0100,1110,1000,0101

    と分割する

    原理

    対象が1bit,2bit,4bitの時は、以下のような感じで、該当部分のビット列だけを抜き出して右にシフトする。

    対象が8bitの時はデータをそのままunsigned char型の配列として扱う。

    対象が16bitの時はデータをそのままunsigned short型の配列として扱い、取得した2byteの前後をswapする。

    //! @file CGetGrayBits.hpp
    //! @brief 1bit,2bit,4bit,8bit,16bitごとに納められたデータを取得する
    
    #pragma once
    
    
    //! @class CGetGrayBits
    //! @brief 配列上のx番目の画素値を取得するクラス
    class CGetGrayBits {
      unsigned char* m_p;        //!< 画素へのポインタ
      int m_depth;               //!< ビット深度
    
      unsigned char m_mask = 0;  //!< depth8未満の時のビットマスク
    public:
    
    
      //! @brief コンストラクタ
      //! @param [in] p 画素配列へのポインタ
      //! @param [in] depth ビット深度。1画素が何ビットで表現されているか
      CGetGrayBits(
        const void* const p,
        const int depth) :
        m_p(static_cast<unsigned char*>(const_cast<void*>(p))),
        m_depth(depth) {
    
        //ビット深度が8未満の時に使用するマスクを作成
        if (m_depth < 8) {
          for (int i = 0; i < m_depth; i++)
            m_mask = (m_mask << 1) | 1;
        }
    
      }
      //! @brief 指定した番号の画素を取得
      //! @param [in] x 画素番号
      //! @return 画素値
      int operator[](const std::size_t x) {
        switch (m_depth) {
        case 1:
        case 2:
        case 4:
          return getgrayLess8(x);
          break;
        case 8:
          return getgray8(x);
          break;
        case 16:
          return getgray16(x);
          break;
        }
        return -1;
      }
      //! @brief 指定した番号の画素を取得(ビット深度==8の時)
      //! @param [in] x 画素番号
      //! @return 画素値をintで表現したもの
      inline int getgray8(const std::size_t x)const {
        return static_cast<int>(m_p[x]);
      }
    
      //! @brief 指定した番号の画素を取得(ビット深度==16の時)
      //! @param [in] x 画素番号
      //! @return 画素値をintで表現したもの
      inline int getgray16(const std::size_t x)const {
        unsigned short t = reinterpret_cast<unsigned short*>(m_p)[x];
        std::swap(
          reinterpret_cast<unsigned char*>(&t)[0],
          reinterpret_cast<unsigned char*>(&t)[1]);
    
        return t;
    
      }
    
      //! @brief 指定した番号の画素を取得(ビット深度<8の時)
      //! @param [in] x 画素番号
      //! @return 画素値をintで表現したもの
      inline int getgrayLess8(const std::size_t x)const {
        // x ドットが何バイト目かを算出 (0開始)
        std::size_t byte = x / (8 / m_depth);
    
        unsigned char c = m_p[byte];
    
        std::size_t pos = x % (8 / m_depth);
    
        int shift = 8 - m_depth - pos * m_depth;
    
        unsigned char mask1 = m_mask << shift;
        unsigned int val = (c & mask1) >> shift;
    
        return val;
      }
    };
    

    使用例

    #pragma once
    
    #include <cstdlib>
    #include <iostream>
    #include <vector>
    #include <bitset>
    
    #include <png.h>
    
    #include <algorithm>
    
    #include "CGetGrayBits.hpp"
    
    #pragma comment(lib,"libpng16.lib")
    
    void out_to_ppm(
      const char* outpath, 
      const int width, 
      const int height, 
      const std::vector<int>& pixels
    );
    
    
    
    //エラーの時強制終了
    void abort_(const char* c) {
      printf(c);
      abort();
    }
    
    //! @brief pngファイル読み込み関数
    //! @param [in] file_name ファイル名
    //! @param [out] width 画像幅(ピクセル)
    //! @param [out] height 画像高さ(ピクセル)
    //! @param [out] color_type RGBかRGBAか...等
    //! @param [out] bit_depth チャンネルのビット数
    //! @param [out] row_pointers 画像データへのポインタのポインタ
    //! @param [out] palette パレット
    //! @param [out] palette_num パレット色の個数
    void read_png(
      const char* file_name,
      int* width,
      int* height,
      png_byte* color_type,
      png_byte* bit_depth,
      png_bytep** row_pointers,
      png_colorp* palette,
      int* palette_num
    ) {
    
      png_byte  header[8];    // 8 is the maximum size that can be checked
    
      FILE* fp = fopen(file_name, "rb");
      if (!fp) {
        abort_("[read_png_file] File could not be opened for reading");
      }
      fread(header, 1, 8, fp);
      if (png_sig_cmp(header, 0, 8)) {
        abort_("[read_png_file] File is not recognized as a PNG file");
      }
    
    
      png_structp png_ptr;
      png_infop info_ptr;
    
      /* initialize stuff */
      png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    
      if (!png_ptr)
        abort_("[read_png_file] png_create_read_struct failed");
    
      info_ptr = png_create_info_struct(png_ptr);
      if (!info_ptr)
        abort_("[read_png_file] png_create_info_struct failed");
    
      if (setjmp(png_jmpbuf(png_ptr)))
        abort_("[read_png_file] Error during init_io");
    
      png_init_io(png_ptr, fp);
      png_set_sig_bytes(png_ptr, 8);
    
      png_read_info(png_ptr, info_ptr);
    
      /////////////////////////////////////////
      // 画像情報の取得
      *width = png_get_image_width(png_ptr, info_ptr);
      *height = png_get_image_height(png_ptr, info_ptr);
      *color_type = png_get_color_type(png_ptr, info_ptr);
      *bit_depth = png_get_bit_depth(png_ptr, info_ptr);
      //
      /////////////////////////////////////////
    
      /////////////////////////////////////////
      // パレットの取得
      if (*color_type & PNG_COLOR_TYPE_PALETTE) {
        png_get_PLTE(png_ptr, info_ptr, palette, palette_num);
      }
      //
      /////////////////////////////////////////
    
      int number_of_passes;
      number_of_passes = png_set_interlace_handling(png_ptr);
      png_read_update_info(png_ptr, info_ptr);
    
      /* read file */
      if (setjmp(png_jmpbuf(png_ptr)))
        abort_("[read_png_file] Error during read_image");
    
      /////////////////////////////////////////
      // 画像の読み込み
      *row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * *height);
    
      for (int y = 0; y < *height; y++)
        (*row_pointers)[y] = (png_byte*)malloc(png_get_rowbytes(png_ptr, info_ptr));
    
      png_read_image(png_ptr, *row_pointers);
      //
      /////////////////////////////////////////
    
    
      fclose(fp);
    }
    int main()
    {
      int width;
      int height;
      png_byte color_type;
      png_byte bit_depth;
      png_bytep* row_pointers;
      png_colorp palette;
      int palette_num;
    
      ////////////////////////////////
      // 画像読み込み
      read_png(
        R"(c:\test\data\snail2.png)",
        &width,
        &height,
        &color_type,
        &bit_depth,
        &row_pointers,
        &palette,
        &palette_num
      );
      //
      ////////////////////////////////
    
      std::cout << "width : " << width << std::endl;
      std::cout << "height: " << height << std::endl;
      std::cout << "colortype: " << (int)color_type << std::endl;
      std::cout << "bitdepth: " << (int)bit_depth << std::endl;
    
      ////////////////////////////////
      //グレイスケール画像だけを対象とする
      if (color_type != (PNG_COLOR_TYPE_GRAY )){
        std::cout << "not support" << std::endl;
        int i;
        std::cin >> i;
        return -1;
      }
      //
      ////////////////////////////////
    
    
      ////////////////////////////////
      // 内容を取得
      std::vector<int> imgvec;
      for (int y = 0; y < height; y++) {
        png_bytep yhead = row_pointers[y]; // 画像の横一列の先頭を取得
    
        CGetGrayBits g(yhead, bit_depth); // 画素取得の準備
    
        for (int x = 0; x < width; x++) {
    
          int index = g[x]; // x番目の画素値をintで取得
    
          imgvec.push_back( index ); // 画素値を保存
        }
    
      }
      //
      ///////////////////////////////////////////////
      ///////////////////////////////////////////////
      // 画像として保存
      char outpath[1024];
      sprintf(outpath, R"(C:\test\out-%d-palette.ppm)",bit_depth);
      out_to_ppm(outpath, width, height, imgvec);
      //
      ///////////////////////////////////////////////
    
    
      ///////////////////////////////////////////////
      // メモリの解放
      for (size_t i = 0; i < (size_t)height; i++) {
        png_bytep yhead = row_pointers[i];
        free(yhead);
      }
      free(row_pointers);
      //
      ///////////////////////////////////////////////
    
      int i;
      std::cin >> i;
    
    }
    
    
    void out_to_ppm(const char* outpath,const int width, const int height, const std::vector<int>& pixels) {
    
      int pixmax = *std::max_element(pixels.begin(), pixels.end());
    
      FILE* fp = fopen(outpath, "wb");
      fprintf(fp, "P3\n%d %d\n%d\n", width, height, 255);
    
      for (size_t y = 0; y < (size_t)height; y++) {
    
        for (size_t x = 0; x < (size_t)width; x++) {
    
          int index = y * width + x;
    
          unsigned char c = pixels[index] / (double)pixmax * 255;
    
          fprintf(fp, "%d %d %d ", c, c, c);
    
        }
        fprintf(fp, "\n");
      }
    
      fclose(fp);
    }
    

    テストデータ

    前回ImageMagickで作成したデータを用いる。