libpng関係でグレースケール画像がほしい。
それもただのグレースケール画像では無く、bit-depthがそれぞれ1bit,2bit,4bit,8bit,16bitのものがほしい。
諸事情により自前で調達することにした。
ImageMagickはコマンドラインから画像を操作するソフトウェア。
ダウンロードは以下:
https://imagemagick.org/script/download.php
Windowsの場合、Q16とついているものを選んだ方がよりよい。
以下の要領でコマンドを実行する
例:
ちなみに、-depthを3や6といった数値にしても、png時のbit depthは勝手に8になる。
以前にも、以下の記事あたりで使ったのだが、メモリデバイスコンテキストをクラス化する話。
//! @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; }
世の中にはたくさんの「簡単に使えるようにクラスにしてまとめました」があるが、そのほとんどは使い物にならないと私は考えている。もちろん自分で作ったものも含めて。
というのも、クラス化というのはある程度の目的や明確な用途があって初めて効果を発揮するものなので、普段自分が使う部分だけが使いやすくなるように作って使いやすくなったように見えてもそれは自分にとってだけなのだ。むしろ提示されたほうは学習コストが無駄にかかる。
さて、薄々気づいている人もいるかと思うが、著者は今ライザのアトリエをやっていてブログを書く暇がガチでない。というわけで、もう暫くできるだけ楽に更新できる内容で繋いでいきたい。
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内に入れることを考える。
これを発展させればクロスプラットフォームなGUIライブラリを作れる(多分)
#pragma once #ifdef DLL_EXPORT_DO /* DLLを作る場合 */ #define __DLL_PORT extern "C" __declspec(dllexport) #else /* DLLを使う場合 */ #define __DLL_PORT extern "C" __declspec(dllimport) #endif
#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();
#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; }
#include <Windows.h> #include <cstdio> #include "../winshow/winshow.h" #pragma comment(lib,"winshow.lib") int main() { return open_window(); }
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 1bit / 4bit PNG_COLOR_TYPE_GRAY 出力
libpng PNG_COLOR_TYPE_PALETTEでパレットを使った出力
libpng パレット色(PNG_COLOR_TYPE_PALETTE)画像の読み込み
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 1bit / 4bit PNG_COLOR_TYPE_GRAY 出力
libpng PNG_COLOR_TYPE_PALETTEでパレットを使った出力
libpng パレット色(PNG_COLOR_TYPE_PALETTE)画像の読み込み
libpng 16bit PNG_COLOR_TYPE_GRAY 読み込み
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 1bit / 4bit PNG_COLOR_TYPE_GRAY 出力
libpng PNG_COLOR_TYPE_PALETTEでパレットを使った出力
libpng パレット色(PNG_COLOR_TYPE_PALETTE)画像の読み込み
libpng 16bit PNG_COLOR_TYPE_GRAY 読み込み
画像は前のプログラムで出力したもの。
#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 1bit / 4bit PNG_COLOR_TYPE_GRAY 出力
libpng PNG_COLOR_TYPE_PALETTEでパレットを使った出力
libpng パレット色(PNG_COLOR_TYPE_PALETTE)画像の読み込み
libpng 16bit PNG_COLOR_TYPE_GRAY 読み込み
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 1bit / 4bit PNG_COLOR_TYPE_GRAY 出力
libpng PNG_COLOR_TYPE_PALETTEでパレットを使った出力
libpng パレット色(PNG_COLOR_TYPE_PALETTE)画像の読み込み
libpng 16bit PNG_COLOR_TYPE_GRAY 読み込み
libpngにおいて、RGBやRGBA以外、特にbit depthが8bit未満の時どんな風に扱うのかの確認。
以下はPNG_COLOR_TYPE_GRAY のbit_depth == 1 についてファイル出力を行う場合。GIMPで開いて対応を確認すると、各ビットが下図のように対応している。
※ write_pngは前回作成した関数。
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 1bit / 4bit PNG_COLOR_TYPE_GRAY 出力
libpng PNG_COLOR_TYPE_PALETTEでパレットを使った出力
libpng パレット色(PNG_COLOR_TYPE_PALETTE)画像の読み込み
libpng 16bit PNG_COLOR_TYPE_GRAY 読み込み
//参考元: //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 1bit / 4bit PNG_COLOR_TYPE_GRAY 出力
libpng PNG_COLOR_TYPE_PALETTEでパレットを使った出力
libpng パレット色(PNG_COLOR_TYPE_PALETTE)画像の読み込み
libpng 16bit PNG_COLOR_TYPE_GRAY 読み込み