ぬの部屋(仮)
nu-no-he-ya
  •      12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    30      
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
         12
    3456789
    10111213141516
    17181920212223
    2425262728  
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    30      
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
       1234
    567891011
    12131415161718
    19202122232425
    26272829   
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
       1234
    567891011
    12131415161718
    19202122232425
    2627282930  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728     
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
       1234
    567891011
    12131415161718
    19202122232425
    2627282930  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
     123456
    78910111213
    14151617181920
    21222324252627
    28      
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
       1234
    567891011
    12131415161718
    19202122232425
    2627282930  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
    1234567
    891011121314
    15161718192021
    22232425262728
           
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    30      
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
         12
    3456789
    10111213141516
    17181920212223
    242526272829 
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    30      
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
        123
    45678910
    11121314151617
    18192021222324
    25262728   
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
       1234
    567891011
    12131415161718
    19202122232425
    2627282930  
           
    1234567
    15161718192021
    293031    
           
         12
    3456789
    10111213141516
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    30      
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
       1234
    567891011
    12131415161718
    19202122232425
    2627282930  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728     
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
          1
    2345678
    9101112131415
    16171819202122
    232425262728 
           
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
  • 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で作成したデータを用いる。

    ImageMagickでカラー画像をn bit Grayscale png画像に変換する

    libpng関係でグレースケール画像がほしい。

    それもただのグレースケール画像では無く、bit-depthがそれぞれ1bit,2bit,4bit,8bit,16bitのものがほしい。

    諸事情により自前で調達することにした。

    ImageMagick

    ImageMagickはコマンドラインから画像を操作するソフトウェア。

    ダウンロードは以下:

    https://imagemagick.org/script/download.php

    Windowsの場合、Q16とついているものを選んだ方がよりよい。

    コマンド

    以下の要領でコマンドを実行する

    >magick convert -threshold 50% -colorspace gray -depth ビット数 元画像名 結果画像名

    例:

    >magick convert -threshold 50% -colorspace gray -depth 1 snail-4729777_640.jpg snail1.png

    元画像

    https://pixabay.com/ja/photos/%E3%82%AB%E3%82%BF%E3%83%84%E3%83%A0%E3%83%AA-%E5%BA%AD-%E5%B7%BB%E3%81%8D%E8%B2%9D-%E8%87%AA%E7%84%B6-4729777/

    変換後画像

    1bit grayscale
    2bit grayscale
    4bit grayscale
    8bit grayscale
    16bit grayscale

    ちなみに、-depthを3や6といった数値にしても、png時のbit depthは勝手に8になる。

    メモリデバイスコンテキストのカプセル化

    以前にも、以下の記事あたりで使ったのだが、メモリデバイスコンテキストをクラス化する話。

    MdcCanvas.hpp

    //! @file MdcCanvas.hpp
    //! @brief メモリデバイスコンテキストのカプセル化
    //! @date 2020/04/22 別コードから移植
    //! @date 2020/04/25 コメント整理
    
    #pragma once
    #include <cassert>
    #include <Windows.h>
    
    namespace szl {
      struct cbit24_t;
      struct cbit32_t;
    
      //! @brief メモリデバイスコンテキストのカプセル化
      //! @tparam PIXEL_BITS 1画素のデータ型。
    
      template<typename PIXEL_BITS>
      class CMdcCanvas
      {
        //CreateDIBSection , DDB , DIB 関連
    
        BITMAPINFO m_bmpInfo;   //!< CreateDIBSectionに渡す構造体
        LPDWORD m_lpPixel;      //!< 画素へのポインタ
        HBITMAP m_hBitmap;      //!< 作成したMDCのビットマップハンドル
        HDC m_hMemDC;           //!< 作成したMDCのハンドル
    
        int m_imagesizemax;     //!< 合計画素数(width*height)
        int m_width;            //!< 画像の幅(ピクセル,自然数)
        int m_height;           //!< 画像の高さ(ピクセル,自然数)
      public:
    
        //! @brief 新しい画像を作成。すでにあるなら現在のものを破棄して再作成
        //! @param [in] width 画像の幅。( > 0)
        //! @param [in] height 画像の高さ。負数の時は左下原点になる
        //! @param [in] hdc CreateCompatibleDCに渡すDCのハンドル。指定しない場合はCreateDCで自動作成
        //! @retval true 作成した
        //! @retval false 作成しなかった
        bool create_screen(int width, int height, HDC hdc=nullptr);
    
        //! @brief 現在使用している画像を削除。削除済みか作成されていないなら何もしない
        //! @return なし
        void delete_screen();
    
        //! @brief 仮想画面の幅を取得
        //! @return 画像幅(自然数)
        int get_width()const { return m_width; }
    
        //! @brief 仮想画面の高さを取得
        //! @return 画像高さ(自然数)
        int get_height()const { return m_height; }
    
    
        //! @brief メモリデバイスコンテキストを取得 (const)
        //! @return メモリデバイスコンテキスト
        const HDC get_mem_dc()const { return m_hMemDC; }
    
        //! @brief メモリデバイスコンテキストを取得
        //! @return メモリデバイスコンテキスト
        HDC get_mem_dc() { return m_hMemDC; }
    
        //! @brief BITMAPINFO構造体を取得
        //! @return BITMAPINFO構造体のコピー
        BITMAPINFO get_BITMAPINFO()const { return m_bmpInfo; }
    
        //! @brief HBITMAPを取得
        //! @return HBITMAPのコピー
        HBITMAP get_HBITMAP()const { return m_hBitmap; }
    
        //! @brief 書き込み用のメモリ領域へのアドレスを取得
        PIXEL_BITS* get_mem_pixel() { return reinterpret_cast<PIXEL_BITS * >(m_lpPixel); }
    
        //! @brief 書き込み用のメモリ領域へのアドレスを取得
        const PIXEL_BITS* get_mem_pixel()const { return reinterpret_cast<PIXEL_BITS*>(m_lpPixel); }
    
        //! @brief 画面に転送を行う
        //! @param [in] hdc 転送先のデバイスコンテキスト
        //! @param [in] rop 転送モード
        //! @return なし
        void bitblt_to(const HDC hdc,const DWORD rop = SRCCOPY)const;
    
        //! @brief 仮想画面が確保されているか
        //! @retval true 使用可能
        //! @retval false 使用不可
        bool is_valid()const { return (m_hBitmap == nullptr) ? false : true; }
    
        //! @brief 一次元配列として画素へアクセスする
        //! @param [in] index 要素番号
        //! @return 画素への参照
        //! @note indexが不正な場合assertする
        PIXEL_BITS& operator[](const int index) {
          assert(index >= 0 && index < m_imagesizemax);
          return get_mem_pixel()[index];
        }
    
        //! @brief 一次元配列として画素へアクセスする(const)
        //! @param [in] index 要素番号
        //! @return 画素への参照
        //! @note indexが不正な場合assertする
        const PIXEL_BITS& operator[](const int index)const {
          assert(index >= 0 && index < m_imagesizemax);
          return get_mem_pixel()[index];
        }
    
        //! @brief 二次元配列として画素へアクセスする
        //! @param [in] x X座標
        //! @param [in] y Y座標
        //! @return 画素への参照
        //! @note 座標が不正な場合assertする
        PIXEL_BITS& operator()(const int x, const int y) {
    
          assert(x >= 0 && y >= 0);
          assert(x < get_width() && y < get_height());
          assert((x + y * get_width()) < m_imagesizemax);
          return get_mem_pixel()[x + y * get_width()];
        }
    
        //! @brief 二次元配列として画素へアクセスする
        //! @param [in] x X座標
        //! @param [in] y Y座標
        //! @return 画素への参照
        //! @note 座標が不正な場合assertする
        const PIXEL_BITS& operator()(const int x, const int y)const {
          assert(x >= 0 && y >= 0);
          assert(x < get_width() && y < get_height());
          assert((x + y * get_width()) < m_imagesizemax);
          return get_mem_pixel()[x + y * get_width()];
    
        }
    
        //! @brief 初期化を行う
        CMdcCanvas(void);
        //! @brief 画像を破棄する
        ~CMdcCanvas(void);
      };
      //! @struct cbit24_t
      //! @brief 1画素 3Byte の場合のデータ型
      struct cbit24_t {
        unsigned char v[3];          //!< データ3Byte
    
        cbit24_t() {}
        cbit24_t(unsigned char r, unsigned char g, unsigned char b) :v{ b,g,r } {}
    
        unsigned char& r() { return v[2]; } //!< 赤を取得
        unsigned char& g() { return v[1]; } //!< 緑を取得
        unsigned char& b() { return v[0]; } //!< 青を取得
      };
    
      //! @struct cbit32_t
      //! @brief 1画素 4Byte の場合のデータ型
      struct cbit32_t {
        unsigned char v[4];          //!< データ3Byte
        cbit32_t() {}
        cbit32_t(unsigned char r, unsigned char g, unsigned char b, unsigned char u = 0) :v{ b,g,r,u } {}
    
        unsigned char& u() { return v[3]; } //!< 未使用領域を取得
        unsigned char& r() { return v[2]; } //!< 赤を取得
        unsigned char& g() { return v[1]; } //!< 緑を取得
        unsigned char& b() { return v[0]; } //!< 青を取得
      };
    
      //! @brief データ型のビット数を返す
      //! @tparam T データ型
      //! @return sizeofの8倍
      template<typename T>
      inline constexpr int bit_count() { return sizeof(T)*8; }
    
      //! @brief データ型のビット数を返す(自分定義3byte用)
      //! @return 24
      template<>inline constexpr int bit_count<cbit24_t>() { return 24; }
    
      //! @brief データ型のビット数を返す(自分定義4byte用)
      //! @return 32
      template<>inline constexpr int bit_count<cbit32_t>() { return 32; }
    
      template<typename PIXEL_BITS>
      CMdcCanvas<PIXEL_BITS>::CMdcCanvas(void)
      {
        m_hBitmap = nullptr;
        m_hMemDC = nullptr;
        m_lpPixel = nullptr;
        m_width = 0;
        m_height = 0;
        m_imagesizemax = 0;
      }
    
    
      template<typename PIXEL_BITS>
      CMdcCanvas<PIXEL_BITS>::~CMdcCanvas(void)
      {
        delete_screen();
      }
    
      template<typename PIXEL_BITS>
      bool CMdcCanvas<PIXEL_BITS>::create_screen(int width, int height, const HDC hdc) {
    
        //指定がおかしければ削除される
        if (width <= 0 || abs(height) <= 0) {
          delete_screen();
          return false;
        }
    
        //既にあるなら削除する
        if (is_valid()) {
          delete_screen();
        }
    
        m_width = width;
        m_height = abs(height);
    
    
    
        //デバイスコンテキストのハンドルの設定
        HDC hdcd;
        if (hdc == nullptr) {
          hdcd = CreateDCA("DISPLAY", 0, 0, 0);
        }
        else {
          hdcd = hdc;
        }
    
        //DIBの情報を設定する
        m_bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
        m_bmpInfo.bmiHeader.biWidth = width;
        m_bmpInfo.bmiHeader.biHeight = -height; //-を指定しないと上下逆になる
        m_bmpInfo.bmiHeader.biPlanes = 1;
        m_bmpInfo.bmiHeader.biBitCount = bit_count<PIXEL_BITS>();
        m_bmpInfo.bmiHeader.biCompression = BI_RGB;
    
        m_hBitmap = CreateDIBSection(hdcd, &m_bmpInfo, DIB_RGB_COLORS, (void**)&m_lpPixel, nullptr, 0);
        m_hMemDC = CreateCompatibleDC(hdcd);
    
        if (hdc == nullptr) {
          DeleteDC(hdcd);
        }
    
        if (m_hBitmap == nullptr || m_hMemDC == nullptr) {
          delete_screen();
          return false;
        }
    
        SelectObject(m_hMemDC, m_hBitmap);
    
    
        m_imagesizemax = m_width * m_height;
    
        return true;
    
      }
    
      template<typename PIXEL_BITS>
      void CMdcCanvas<PIXEL_BITS>::delete_screen() {
        if (m_hBitmap == nullptr)
          return;
    
        DeleteDC(m_hMemDC);
        DeleteObject(m_hBitmap);
        m_lpPixel = nullptr;
        m_hBitmap = nullptr;
        m_hMemDC = nullptr;
    
        m_width = 0;
        m_height = 0;
        m_imagesizemax = 0;
      }
    
    
      template<typename PIXEL_BITS>
      void CMdcCanvas<PIXEL_BITS>::bitblt_to(const HDC hdc,const DWORD rop)const {
        BitBlt(hdc, 0, 0, get_width(), get_height(), m_hMemDC, 0, 0, rop);
      }
    
      using CMdcCanvas24 = CMdcCanvas<cbit24_t>;
      using CMdcCanvas32 = CMdcCanvas<cbit32_t>;
    
    }
    

    使い方

    #include<windows.h>
    
    #include "MdcCanvas.hpp"
    
    //メモリデバイスコンテキスト作成
    szl::CMdcCanvas24 vscr;
    
    
    LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
      HDC hdc;
      switch (msg) {
      case WM_SIZE:
        InvalidateRect(hwnd, nullptr, TRUE);
        break;
      case WM_PAINT:
        PAINTSTRUCT ps;
        hdc = BeginPaint(hwnd, &ps);
        vscr.bitblt_to(hdc);//画面に表示
        EndPaint(hwnd, &ps);
        break;
      case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
      }
      return DefWindowProc(hwnd, msg, wp, lp);
    }
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
      PSTR lpCmdLine, int nCmdShow) {
      HWND hwnd;
      WNDCLASS winc;
      MSG msg;
    
      winc.style = CS_HREDRAW | CS_VREDRAW;
      winc.lpfnWndProc = WndProc;
      winc.cbClsExtra = winc.cbWndExtra = 0;
      winc.hInstance = hInstance;
      winc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
      winc.hCursor = LoadCursor(NULL, IDC_ARROW);
      winc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
      winc.lpszMenuName = NULL;
      winc.lpszClassName = TEXT("DC_TEST");
    
      if (!RegisterClass(&winc)) return 0;
    
      hwnd = CreateWindow(
        TEXT("DC_TEST"), TEXT("mem dc test"),
        WS_OVERLAPPEDWINDOW | WS_VISIBLE,
        CW_USEDEFAULT, CW_USEDEFAULT,
        300, 300,
        NULL, NULL,
        hInstance, NULL
      );
    
    
      //画像作成
      HDC dc = GetDC(hwnd);
      vscr.create_screen(200, 100,dc);
      ReleaseDC(hwnd, dc);
    
    
      int i = 0;
    
      //メモリに直接アクセス
      for (int i = 0; i < 200 * 100; i++) {
        vscr.get_mem_pixel()[i] = szl::cbit24_t(100, 255, 100);
      }
    
      //GDIオブジェクトとして扱う
      HPEN hp = CreatePen(PS_SOLID, 5, RGB(255, 0, 0));
      HPEN oldp = (HPEN)SelectObject(vscr.get_mem_dc(), hp);
      MoveToEx(vscr.get_mem_dc(), -10, -10,nullptr);
      LineTo(vscr.get_mem_dc(), 50, 80);
      SelectObject(vscr.get_mem_dc(), oldp);
      DeleteObject(hp);
    
      //二次元配列の形でアクセス
      vscr(0, 0) = szl::cbit24_t(255, 0, 0);
      vscr(0, 1) = szl::cbit24_t(255, 0, 0);
      vscr(0, 2) = szl::cbit24_t(0, 255, 0);
      vscr(0, 3) = szl::cbit24_t(0, 0, 255);
    
      if (hwnd == NULL) return 0;
    
      while (GetMessage(&msg, NULL, 0, 0)) DispatchMessage(&msg);
      return msg.wParam;
    }
    

    雑談

    世の中にはたくさんの「簡単に使えるようにクラスにしてまとめました」があるが、そのほとんどは使い物にならないと私は考えている。もちろん自分で作ったものも含めて。

    というのも、クラス化というのはある程度の目的や明確な用途があって初めて効果を発揮するものなので、普段自分が使う部分だけが使いやすくなるように作って使いやすくなったように見えてもそれは自分にとってだけなのだ。むしろ提示されたほうは学習コストが無駄にかかる。

    WinMainを使わずにウィンドウを開く

    雑談

    さて、薄々気づいている人もいるかと思うが、著者は今ライザのアトリエをやっていてブログを書く暇がガチでない。というわけで、もう暫くできるだけ楽に更新できる内容で繋いでいきたい。

    GetModuleHandle(0)でインスタンスハンドルを取得

    Windowsプログラミングはエントリポイントが原則としてWinMainになる。

    とはいえmainからは動かないかというとそんなことはない。

    ウィンドウを開くために必要なhInstanceはGetModuleHandle関数に0を渡すことで取得できる。

    #include <Windows.h>
    #include <cstdio>
    
    LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
    
    
    int main()
    {
      //インスタンスハンドルの取得
      HINSTANCE hInstance = GetModuleHandle(0);
    
      HWND hwnd;
      WNDCLASS winc;
    
      winc.style = CS_HREDRAW | CS_VREDRAW;
      winc.lpfnWndProc = WndProc;
      winc.cbClsExtra = winc.cbWndExtra = 0;
      winc.hInstance = hInstance;
      winc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
      winc.hCursor = LoadCursor(NULL, IDC_ARROW);
      winc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
      winc.lpszMenuName = NULL;
      winc.lpszClassName = TEXT("WITHOUT_WINMAIN");
    
      if (!RegisterClass(&winc)) return 0;
    
    
      hwnd = CreateWindow(
        TEXT("WITHOUT_WINMAIN"), TEXT("window without WinMain"),
        WS_OVERLAPPEDWINDOW | WS_VISIBLE,
        100, 100, 100, 100, NULL, NULL,
        hInstance, NULL
      );
    
      if (hwnd == NULL) return 0;
    
      printf("success\n"); // これを入れるとコンソールに表示される
      FreeConsole(); // これを入れるとコンソール自体が消える
    
    
      MSG msg;
      while (GetMessage(&msg, NULL, 0, 0)) {
        DispatchMessage(&msg);
      }
      return msg.wParam;
    
    
    }
    LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
      switch (msg) {
      case WM_LBUTTONDOWN:
        MessageBox(hwnd, L"hello", 0, 0);
        break;
      case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
      }
      return DefWindowProc(hwnd, msg, wp, lp);
    }
    

    DLL化

    これだけだとあまりにも記事として寂しいので、ウィンドウ生成をDLL内に入れることを考える。

    これを発展させればクロスプラットフォームなGUIライブラリを作れる(多分)

    DLL側

    dllport.h

    #pragma once
    
    #ifdef  DLL_EXPORT_DO
    /*  DLLを作る場合   */
    #define  __DLL_PORT  extern "C" __declspec(dllexport)   
    #else
    /*  DLLを使う場合   */
    #define  __DLL_PORT  extern "C" __declspec(dllimport)   
    #endif
    

    winshow.h

    #pragma once
    
    #include<Windows.h>
    
    #include "../dllport.h"
    
    LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
    
    BOOL APIENTRY DllMain(HMODULE hModule,DWORD  ul_reason_for_call,LPVOID lpReserved);
    
    __DLL_PORT int open_window();
    

    winshow.cpp

    #include "winshow.h"
    
    
    BOOL APIENTRY DllMain(HMODULE hModule,
      DWORD  ul_reason_for_call,
      LPVOID lpReserved
    )
    {
      return TRUE;
    }
    LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
      switch (msg) {
      case WM_LBUTTONDOWN:
        MessageBox(hwnd, L"hello in dll", 0, 0);
        break;
      case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
      }
      return DefWindowProc(hwnd, msg, wp, lp);
    }
    
    
    int open_window() {
      //インスタンスハンドルの取得
      HINSTANCE hInstance = GetModuleHandle(0);
    
      HWND hwnd;
      WNDCLASS winc;
    
      winc.style = CS_HREDRAW | CS_VREDRAW;
      winc.lpfnWndProc = WndProc;
      winc.cbClsExtra = winc.cbWndExtra = 0;
      winc.hInstance = hInstance;
      winc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
      winc.hCursor = LoadCursor(NULL, IDC_ARROW);
      winc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
      winc.lpszMenuName = NULL;
      winc.lpszClassName = TEXT("WITHOUT_WINMAIN");
    
      if (!RegisterClass(&winc)) return 0;
    
    
      hwnd = CreateWindow(
        TEXT("WITHOUT_WINMAIN"), TEXT("window without WinMain"),
        WS_OVERLAPPEDWINDOW | WS_VISIBLE,
        100, 100, 100, 100, NULL, NULL,
        hInstance, NULL
      );
    
      if (hwnd == NULL) return 0;
    
      MSG msg;
      while (GetMessage(&msg, NULL, 0, 0)) {
        DispatchMessage(&msg);
      }
      return msg.wParam;
    
    }
    

    exe側

    #include <Windows.h>
    #include <cstdio>
    
    #include "../winshow/winshow.h"
    
    #pragma comment(lib,"winshow.lib")
    
    int main()
    {
      return open_window();
    }
    

    libpng 16bit PNG_COLOR_TYPE_GRAY 出力

    16bit Grayscale画像の場合、以下のコードのように、2byte値の前後を交換して与えなければいけない。

    ソースコード

    #include <cstdlib>
    #include<algorithm>
    
    #include <png.h>
    
    #pragma comment(lib,"libpng16d.lib")
    
    //エラーの時強制終了
    void abort_(const char* c) {
      printf(c);
      abort();
    }
    
    int main()
    {
      int width;
      int height;
      png_byte color_type = PNG_COLOR_TYPE_GRAY;// グレイスケール
      png_byte bit_depth = 16;// 2バイト
    
      png_bytep* row_pointers;
    
      ////////////////////////////////
      // 画像作成
      height = 1;
      width = 4;
      row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * height);
      row_pointers[0] = (png_byte*)malloc(4*2);
      png_byte* xhead;
      xhead = row_pointers[0];
      unsigned short* p0 = (unsigned short*)&xhead[0];// 1byte置きに画素を配置する
      unsigned short* p1 = (unsigned short*)&xhead[2];
      unsigned short* p2 = (unsigned short*)&xhead[4];
      unsigned short* p3 = (unsigned short*)&xhead[6];
    
      unsigned short v;
    
      *p0 = 0;
    
      v = USHRT_MAX / 4;// 画素値を設定
      std::swap( ((unsigned char*)(&v))[0], ((unsigned char*)(&v))[1]);// 2byte画素値の前後を入れ替える
      *p1 = v;
    
      v = USHRT_MAX/2;// 画素値を設定
      std::swap(((unsigned char*)(&v))[0], ((unsigned char*)(&v))[1]);// 2byte画素値の前後を入れ替える
      *p2 = v;
    
      v = USHRT_MAX;// 画素値を設定
      std::swap(((unsigned char*)(&v))[0], ((unsigned char*)(&v))[1]);// 2byte画素値の前後を入れ替える
      *p3 = v;
    
      //
      ////////////////////////////////
      const char* file_name = R"(c:\test\test_16bit_1x4.png)";
    
      /* create file */
      FILE* fp = fopen(file_name, "wb");
      if (!fp)
        abort_("[write_png_file] File could not be opened for writing");
    
      png_structp png_ptr;
      /* initialize stuff */
      png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    
      if (!png_ptr)
        abort_("[write_png_file] png_create_write_struct failed");
    
    
      png_infop info_ptr;
    
      info_ptr = png_create_info_struct(png_ptr);
      if (!info_ptr)
        abort_("[write_png_file] png_create_info_struct failed");
    
      if (setjmp(png_jmpbuf(png_ptr)))
        abort_("[write_png_file] Error during init_io");
    
      png_init_io(png_ptr, fp);
    
      /////////////////////////////////////
      //ヘッダの設定
      if (setjmp(png_jmpbuf(png_ptr)))
        abort_("[write_png_file] Error during writing header");
    
      png_set_IHDR(png_ptr, info_ptr, width, height,
        bit_depth, color_type, PNG_INTERLACE_NONE,
        PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
    
      png_write_info(png_ptr, info_ptr);
      //
      /////////////////////////////////////
    
    
      /////////////////////////////////////
      // 画像のファイル出力
      if (setjmp(png_jmpbuf(png_ptr)))
        abort_("[write_png_file] Error during writing bytes");
    
      png_write_image(png_ptr, row_pointers);
    
      // end write
      if (setjmp(png_jmpbuf(png_ptr)))
        abort_("[write_png_file] Error during end of write");
    
      png_write_end(png_ptr, NULL);
      //
      /////////////////////////////////////
    
    
      fclose(fp);
    
    
      ///////////////////////////////////////////////
      // メモリの解放
      for (size_t i = 0; i < (size_t)height; i++) {
        png_bytep yhead = row_pointers[i];
        free(yhead);
      }
      free(row_pointers);
      //
      ///////////////////////////////////////////////
    
    }
    

    libpngを試す(CMakeビルド)

    libpngを試す(ファイル読み込み)

    libpngを試す(ファイル書き込み)

    libpng 1bit / 4bit PNG_COLOR_TYPE_GRAY 出力

    libpng PNG_COLOR_TYPE_PALETTEでパレットを使った出力

    libpng 1bit Grayscale画像の読み込み

    libpng パレット色(PNG_COLOR_TYPE_PALETTE)画像の読み込み

    libpng 16bit PNG_COLOR_TYPE_GRAY 読み込み

    libpng 16bit PNG_COLOR_TYPE_GRAY 出力

    setjmp / longjmp

    libpng 16bit PNG_COLOR_TYPE_GRAY 読み込み

    前記事で出力した16bit grayscale画像を読み込む。

    この場合も入力画像の一画素の前後を反転させなければならない。

    読み込んだ画像
    なぜかぼけるので拡大図
    #pragma once
    
    #include <cstdlib>
    #include <iostream>
    #include <bitset>
    
    #include <png.h>
    
    #pragma comment(lib,"libpng16d.lib")
    
    //エラーの時強制終了
    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 画像データへのポインタのポインタ
    void read_png(
      const char* file_name,
      int* width,
      int* height,
      png_byte* color_type,
      png_byte* bit_depth,
      png_bytep** row_pointers
    
    ) {
    
      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);
      //
      /////////////////////////////////////////
    
      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;
    
      ////////////////////////////////
      // 画像読み込み
      read_png(
        R"(c:\test\test_16bit_1x4.png)",
        &width,
        &height,
        &color_type,
        &bit_depth,
        &row_pointers
      );
      //
      ////////////////////////////////
    
      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;
    
      ////////////////////////////////
      //今回は1ビットグレイスケール画像だけを対象にする
      if ((color_type != PNG_COLOR_TYPE_GRAY) ||
        (bit_depth != 16)
        ) {
        return -1;
      }
      //
      ////////////////////////////////
    
    
      ////////////////////////////////
      // 内容を表示
      png_bytep yhead = row_pointers[0];
      png_bytep xpix = yhead;
      unsigned short* pp = (unsigned short*)xpix;
    
      std::swap(((unsigned char*)(&pp[0]))[0], ((unsigned char*)(&pp[0]))[1]);
      std::swap(((unsigned char*)(&pp[1]))[0], ((unsigned char*)(&pp[1]))[1]);
      std::swap(((unsigned char*)(&pp[2]))[0], ((unsigned char*)(&pp[2]))[1]);
      std::swap(((unsigned char*)(&pp[3]))[0], ((unsigned char*)(&pp[3]))[1]);
    
      std::cout << "[0]" << std::bitset<16>(pp[0]) << std::endl;
      std::cout << "[1]" << std::bitset<16>(pp[1]) << std::endl;
      std::cout << "[2]" << std::bitset<16>(pp[2]) << std::endl;
      std::cout << "[3]" << std::bitset<16>(pp[3]) << std::endl;
      //
      ///////////////////////////////////////////////
      ///////////////////////////////////////////////
      // メモリの解放
      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;
    
    }
    

    実行結果

    libpngを試す(CMakeビルド)

    libpngを試す(ファイル読み込み)

    libpngを試す(ファイル書き込み)

    libpng 1bit / 4bit PNG_COLOR_TYPE_GRAY 出力

    libpng PNG_COLOR_TYPE_PALETTEでパレットを使った出力

    libpng 1bit Grayscale画像の読み込み

    libpng パレット色(PNG_COLOR_TYPE_PALETTE)画像の読み込み

    libpng 16bit PNG_COLOR_TYPE_GRAY 読み込み

    libpng 16bit PNG_COLOR_TYPE_GRAY 出力

    setjmp / longjmp