ぬの部屋(仮)
nu-no-he-ya
  •    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
           
  • 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

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

    2bit パレット使用の画像を読み込んだ例。

    入力画像

    実行結果

    ソースコード

    #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 画像データへのポインタのポインタ
    //! @retval true 成功
    //! @retval false 中断
    bool 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);
      //
      /////////////////////////////////////////
    
      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 | PNG_COLOR_TYPE_PALETTE)) {
    
        /////////////////////////////////////////
        // パレット取得
        png_colorp palette;
        int num;
        png_get_PLTE(png_ptr, info_ptr, &palette, &num);
    
        /////////////////////////////////////////
        // パレット内容表示
        std::cout << std::endl << "palette ( " << num << " )" << std::endl;
        for (int i = 0; i < num; i++) {
          std::cout <<
            "[" << std::bitset<2>(i) << "] "
            << "("
            << (int)palette[i].red << ","
            << (int)palette[i].green << ","
            << (int)palette[i].blue <<
            ")" << std::endl;
        }
    
      }
      /////////////////////////////////////////
      else {
        fclose(fp);
        return false;
      }
    
    
    
      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);
    
      return true;
    
    }
    int main()
    {
      int width;
      int height;
      png_byte color_type;
      png_byte bit_depth;
    
      png_bytep* row_pointers;
    
      bool ret;
      ////////////////////////////////
      // 画像読み込み
      ret = read_png(
        R"(c:\test\test_2bit_palette_1x8.png)",
        &width,
        &height,
        &color_type,
        &bit_depth,
        &row_pointers
      );
      //
      ////////////////////////////////
    
      if (ret == false) {
        return -1;
      }
    
      ////////////////////////////////
      // 内容を表示
      png_bytep yhead = row_pointers[0];
      png_bytep xpix = yhead;
      std::cout << std::endl;
      std::cout << std::bitset<8>(xpix[0]);
      std::cout << std::bitset<8>(xpix[1]) << 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;
    
    
      return 0;
    
    }
    

    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 1bit Grayscale画像の読み込み

    テスト用の画像 ( 1 x 16 )

    画像は前のプログラムで出力したもの。

    ソースコード

    #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_bitdepth_1_notpalette_1x16px.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 != 1)
        ) {
        return -1;
      }
      //
      ////////////////////////////////
    
      ////////////////////////////////
      // 内容を表示
      png_bytep yhead = row_pointers[0];
      png_bytep xpix = yhead;
      std::cout << "[0]" << std::bitset<8>(xpix[0]) << std::endl;
      std::cout << "[1]" << std::bitset<8>(xpix[1]) << 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

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

    概要

    GrayScaleの値をパレット色に置き換える。

    ソースコード

    Color Typeを PNG_COLOR_TYPE_GRAY | PNG_COLOR_TYPE_PALETTE

    あとは png_set_PLTE でパレットを設定する。

    それ以外はRGBやただのグレースケールの時と変わらない。

    #include <cstdlib>
    
    #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_COLOR_TYPE_PALETTE;
      png_byte bit_depth = 1;
    
    
      png_bytep* row_pointers;
    
      ////////////////////////////////
      // 画像作成
      height = 1;
      width = 8;
      row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * height);
      row_pointers[0] = (png_byte*)malloc(1);
      png_byte* xhead;
      xhead = row_pointers[0];
      xhead[0] = 0b01010111;
      //
      ////////////////////////////////
    

    const char* file_name = R"(c:\test\test_palette.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_colorp palette = nullptr;
    
      palette = (png_colorp)png_malloc(png_ptr, sizeof(png_color) * 2);
      palette[0].red = 255;
      palette[0].green = 0;
      palette[0].blue = 0;
      palette[1].red = 0;
      palette[1].green = 255;
      palette[1].blue = 0;
    
      png_set_PLTE(png_ptr, info_ptr, palette, 2);
      //////
      ////////////////////
    
      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 1bit / 4bit PNG_COLOR_TYPE_GRAY 出力

    libpngにおいて、RGBやRGBA以外、特にbit depthが8bit未満の時どんな風に扱うのかの確認。

    以下はPNG_COLOR_TYPE_GRAY のbit_depth == 1 についてファイル出力を行う場合。GIMPで開いて対応を確認すると、各ビットが下図のように対応している。

    ※ write_pngは前回作成した関数。

    例1 bit depth == 1 の場合

    例2 bit depth == 4 の場合

    PNG_COLOR_TYPE_GRAY対応のBit depth

    http://www.libpng.org/pub/png/libpng-1.2.5-manual.html

    PNG_COLOR_TYPE_GRAYに対応するbit depthは

    (bit depths 1, 2, 4, 8, 16)

    コード全体

    #include <iostream>
    
    #include <png.h>
    
    #pragma comment(lib,"libpng16d.lib")
    
    
    //エラーの時強制終了
    void abort_(const char* c) {
      printf(c);
      abort();
    }
    
    
    //! @brief pngファイル書き込み関数
    //! @param [in] file_name ファイル名
    //! @param [in] width 画像幅(ピクセル)
    //! @param [in] height 画像高さ(ピクセル)
    //! @param [in] color_type RGBかRGBAか...等
    //! @param [in] bit_depth チャンネルのビット数
    //! @param [in] row_pointers 画像データへのポインタ
    void write_png(
      const char* file_name,
      const int width,
      const int height,
      const int color_type,
      const int bit_depth,
      const png_bytepp row_pointers
    )
    {
      /* 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);
    }
    
    int main()
    {
      int width;
      int height;
      png_byte color_type = PNG_COLOR_TYPE_GRAY;
      png_byte bit_depth = 4;
    
      png_bytep* row_pointers;
    
      ////////////////////////////////
      // 画像作成
      height = 1;
      width = 5;
      row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * height);
      row_pointers[0] = (png_byte*)malloc(3);
      png_byte* xhead;
      xhead = row_pointers[0];
      xhead[0] = 0xFC;  xhead[1] = 0xA7; xhead[2] = 0x3F;
    
      //
      ////////////////////////////////
    
    
      ////////////////////////////////
      // 画像書き込み
      write_png(
        R"(c:\test\test_bitdepth.png)",
        width,
        height,
        color_type,
        bit_depth,
        row_pointers
      );
      //
      ////////////////////////////////
    
    
      ///////////////////////////////////////////////
      // メモリの解放
      for (size_t i = 0; i < (size_t)height; i++) {
        png_bytep yhead = row_pointers[i];
        free(yhead);
      }
      free(row_pointers);
      //
      ///////////////////////////////////////////////
    
      std::cout << "fin" << std::endl;
    
    }
    

    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を試す(ファイル書き込み)

    ソースコード

    //参考元:
    //http://zarb.org/~gc/html/libpng.html
    
    
    #include <iostream>
    
    #include <png.h>
    
    #pragma comment(lib,"libpng16.lib")
    
    
    
    //エラーの時強制終了
    void abort_(const char* c) {
      printf(c);
      abort();
    }
    
    
    //! @brief pngファイル書き込み関数
    //! @param [in] file_name ファイル名
    //! @param [in] width 画像幅(ピクセル)
    //! @param [in] height 画像高さ(ピクセル)
    //! @param [in] color_type RGBかRGBAか...等
    //! @param [in] bit_depth チャンネルのビット数
    //! @param [in] row_pointers 画像データへのポインタ
    void write_png(
      const char* file_name,
      const int width,
      const int height,
      const int color_type,
      const int bit_depth,
      const png_bytepp row_pointers
    )
    {
      /* 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);
    }
    
    

    int
    main() { int width; int height; png_byte color_type = PNG_COLOR_TYPE_RGB_ALPHA; //RGBA png_byte bit_depth = 8; // 1 pixel == 8bit * 4 png_bytep* row_pointers;
      ////////////////////////////////
      // 画像作成
      height = 256;
      width = 128;
      row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * height);
      for (int y = 0; y < height; y++) {
        row_pointers[y] = (png_byte*)malloc(width * 4);
    
        png_byte* xhead = row_pointers[y];
    
        for (int x = 0; x < width; x++) {
    
          png_byte r = y;
          png_byte g = x*2;
          png_byte b = 128;
          png_byte a = 128 + (255 - r) / 2;
    
          png_byte* px = xhead + x * 4;
          px[0] = r;
          px[1] = g;
          px[2] = b;
          px[3] = a;
        }
      }
      //
      ////////////////////////////////
    
          
      ////////////////////////////////
      // 画像書き込み
      write_png(
        R"(c:\test\test.png)",
        width,
        height,
        color_type,
        bit_depth,
        row_pointers
      );
      //
      ////////////////////////////////
    
          
      ///////////////////////////////////////////////
      // メモリの解放
      for (size_t i = 0; i < (size_t)height; i++) {
        png_bytep yhead = row_pointers[i];
        free(yhead);
      }
      free(row_pointers);
      //
      ///////////////////////////////////////////////
      std::cout << "fin" << std::endl;
    
    }
    

    実行結果

    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