2022/07/19(火) [n年前の日記]
#1 [prog] cmdline.hを少し試用
C++で書いたコマンドラインツールに、コマンドラインオプションを渡して、処理が変わるようにしたい。昨日、getopt() や getopt_long() を使って試してみたけど、それらは記述が面倒だなと…。ヘルプメッセージは自分で用意しないといけないし。
もっと便利なライブラリは無いのかなとググったら、cmdline.h というライブラリがあることを知った。ヘッダーファイル1つを inlcude するだけで使えるらしい。
_C++向け簡易コマンドラインパーザ cmdline - 純粋関数型雑記帳
_tanakh/cmdline: A Command Line Parser
_C++版のgetopt - Qiita
興味が湧いたので、手元で動作確認。動作確認環境は、Windows10 x64 21H2 + MSYS2 + MinGW64 + g++ 12.1.0。
_getopt_sample2.cpp
コンパイルは以下。
実行。
記述量も少なくて済むし、ヘルプメッセージも自動生成してくれるし、ヘッダファイル1つだから使うのも楽だし、静的リンクもしやすい。これは便利だなと…。
もっと便利なライブラリは無いのかなとググったら、cmdline.h というライブラリがあることを知った。ヘッダーファイル1つを inlcude するだけで使えるらしい。
_C++向け簡易コマンドラインパーザ cmdline - 純粋関数型雑記帳
_tanakh/cmdline: A Command Line Parser
_C++版のgetopt - Qiita
興味が湧いたので、手元で動作確認。動作確認環境は、Windows10 x64 21H2 + MSYS2 + MinGW64 + g++ 12.1.0。
_getopt_sample2.cpp
#include "cmdline.h" #include <stdio.h> #include <iostream> int main(int argc, char *argv[]) { // parse options cmdline::parser a; a.add<std::string>("input", 'i', "Input png image", true, ""); a.add<std::string>("output", 'o', "Output png image", true, ""); a.add<std::string>("palette", 'p', "Palette (.png or .gpl)", false, ""); a.add<int>("dither", 'd', "Dither map 2/4/8 (2x2/4x4/8x8)", false, 4); a.parse_check(argc, argv); // get options auto infile = a.get<std::string>("input").c_str(); auto outfile = a.get<std::string>("output").c_str(); auto palette = a.get<std::string>("palette").c_str(); auto dither = a.get<int>("dither"); if (0) { std::cout << "input : " << infile << std::endl; std::cout << "output : " << outfile << std::endl; std::cout << "palette : " << palette << std::endl; std::cout << "dither : " << dither << std::endl; } else { printf("input : [%s]\n", infile); printf("output : [%s]\n", outfile); printf("palette : [%s]\n", palette); printf("dither : %d\n", dither); } if (strlen(infile) == 0 || strlen(outfile) == 0) { // Not set infile or outfile printf("%s\n", a.usage().c_str()); return 1; } if (strlen(palette) == 0) { // Not set palette printf("Use default palette\n"); } if (!(dither == 2 || dither == 4 || dither == 8)) { printf("Error : Unknown dither mode = %d\n\n", dither); printf("%s\n", a.usage().c_str()); return 1; } return 0; }
コンパイルは以下。
g++ getopt_sample2.cpp -o getopt_sample2.exe -static -lstdc++ -lgcc -lwinpthread -lm
実行。
$ ./getopt_sample2.exe --help usage: D:\...\getopt_sample2.exe --input=string --output=string [options] ... options: -i, --input Input png image (string) -o, --output Output png image (string) -p, --palette Palette (.png or .gpl) (string [=]) -d, --dither Dither map 2/4/8 (2x2/4x4/8x8) (int [=4]) -?, --help print this message $ ./getopt_sample2.exe -i in.png -o out.png -d 8 input : [in.png] output : [out.png] palette : [] dither : 8 Use default palette $ ./getopt_sample2.exe --input in.png --output out.png --dither 8 input : [in.png] output : [out.png] palette : [] dither : 8 Use default palette
記述量も少なくて済むし、ヘルプメッセージも自動生成してくれるし、ヘッダファイル1つだから使うのも楽だし、静的リンクもしやすい。これは便利だなと…。
[ ツッコむ ]
#2 [prog] パレットデータ相当を読み込みたい
C++で、パレットデータ相当を読み込みたい。以下のような処理をしたい。
ということで、一応そういう処理を書いてみた。
動作確認環境は、Windows10 x64 21H2 + MSYS2 + MinGW64 + g++ 12.1.0。
画像の読み込みには、stbライブラリ(stb_image.h) を使わせてもらった。ありがたや。
_nothings/stb: stb single-file public domain libraries for C/C++
- png画像を読み込んで、画像内で使われているRGB値を数え上げて、パレットデータ相当として扱う。
- あるいは、テキストデータである .gplファイル(GIMP Paletteファイル)を読み込んで、列挙されているRGB値をパレットデータとして扱う。
ということで、一応そういう処理を書いてみた。
動作確認環境は、Windows10 x64 21H2 + MSYS2 + MinGW64 + g++ 12.1.0。
画像の読み込みには、stbライブラリ(stb_image.h) を使わせてもらった。ありがたや。
_nothings/stb: stb single-file public domain libraries for C/C++
◎ ソース。 :
ソースは以下。
_loadpalette.cpp
コンパイルは以下。
実行は以下。
.gpl も .png も読み込めているように見える。
動作確認に使った .gpl、.png は以下。
_def16.gpl
_def16.png
_loadpalette.cpp
// load palette (.gpl or .png) #include <map> #include <string.h> /* use stb library nothings/stb: stb single-file public domain libraries for C/C++ https://github.com/nothings/stb */ #define STB_IMAGE_STATIC #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" #define CBUF_N 1024 #define TMP_PAL_NUM 1024 unsigned int *load_palette_from_gpl(char *infile, int *len_pal) { unsigned int *pal = nullptr; unsigned int tmp_pal[TMP_PAL_NUM]; FILE *fp; char str[CBUF_N]; unsigned int r, g, b; int idx = 0; for (int i = 0; i < TMP_PAL_NUM; i++) tmp_pal[i] = 0; fp = fopen(infile, "r"); if (fp == NULL) { printf("Error : Can not open %s\n", infile); *len_pal = 0; return nullptr; } while (fgets(str, CBUF_N, fp) != NULL) { str[strlen(str) - 1] = '\0'; if (strncmp("GIMP Palette", str, 12) == 0) continue; if (strncmp("Name:", str, 5) == 0) continue; if (strncmp("Columns", str, 7) == 0) continue; if (strncmp("#", str, 1) == 0) continue; sscanf(str, "%d %d %d", &r, &g, &b); // printf("%s", str); // printf("\t\tRGB : (%d, %d, %d)\n", r, g, b); tmp_pal[idx] = (r << 16) | (g << 8) | b; idx++; } fclose(fp); *len_pal = idx; pal = new unsigned int[idx]; for (int i = 0; i < idx; i++) pal[i] = tmp_pal[i]; return pal; } unsigned int *load_palette_from_img(char *infile, int *len_pal) { unsigned int *pal = nullptr; std::map<unsigned int, unsigned int> pal_map; // load image unsigned char *pixels; int w; int h; int bpp; pixels = stbi_load(infile, &w, &h, &bpp, 0); if (pixels == NULL) { printf("Error : Can not load %s\n", infile); *len_pal = 0; return nullptr; } for (int y = 0; y < h; y++) { int yofs = y * w * bpp; for (int x = 0; x < w; x++) { int i = x * bpp + yofs; unsigned int col = (pixels[i + 0] << 16) | (pixels[i + 1] << 8) | pixels[i + 2]; pal_map[col] += 1; } } stbi_image_free(pixels); int n = pal_map.size(); // pal = (unsigned int *)malloc(sizeof(unsigned int) * n); pal = new unsigned int[n]; int j = 0; for (auto i = pal_map.begin(); i != pal_map.end(); i++) { pal[j] = i->first; j++; } // printf("Count palette = %d\n", j); *len_pal = j; return pal; } unsigned int *load_palette(char *infile, int *len_pal) { unsigned int *pal = nullptr; int n; char *ext = strrchr(infile, '.'); if (stricmp(".gpl", ext) == 0) { // load from .gpl (GIMP palette file) pal = load_palette_from_gpl(infile, &n); } else { // load from .png pal = load_palette_from_img(infile, &n); } *len_pal = n; return pal; } int main(int argc, char **argv) { if (argc != 2) { printf("Usage:\n %s PALETTE\n\n PALETTE (.png or .gpl)\n", argv[0]); return 0; } auto palfile = argv[1]; printf("Palette : %s\n", palfile); int len_pal = 0; unsigned int *my_pal = load_palette(palfile, &len_pal); if (my_pal == nullptr) { printf("Error : Load failure palette [%s]\n", palfile); return 1; } printf("Palette size = %d\n", len_pal); // dump palette data for (int i = 0; i < len_pal; i++) printf("%06X, ", my_pal[i]); printf("\n"); for (int i = 0; i < len_pal; i++) { unsigned int col = my_pal[i]; unsigned int r = (col >> 16) & 0x0ff; unsigned int g = (col >> 8) & 0x0ff; unsigned int b = col & 0x0ff; printf("{%d, %d, %d}, ", r, g, b); } printf("\n"); delete my_pal; }
コンパイルは以下。
g++ loadpalette.cpp -o loadpalette.exe -static -lstdc++ -lgcc -lwinpthread -lm
実行は以下。
$ ./loadpalette.exe def16.gpl Palette : def16.gpl Palette size = 16 080000, 201A0B, 432817, 492910, 234309, 5D4F1E, 9C6B20, A9220F, 2B347C, 2B7409, D0CA40, E8A077, 6A94AB, D5C4B3, FCE76E, FCFAE2, {8, 0, 0}, {32, 26, 11}, {67, 40, 23}, {73, 41, 16}, {35, 67, 9}, {93, 79, 30}, {156, 107, 32}, {169, 34, 15}, {43, 52, 124}, {43, 116, 9}, {208, 202, 64}, {232, 160, 119}, {106, 148, 171}, {213, 196, 179}, {252, 231, 110}, {252, 250, 226}, $ ./loadpalette.exe def16.png Palette : def16.png Palette size = 16 080000, 201A0B, 234309, 2B347C, 2B7409, 432817, 492910, 5D4F1E, 6A94AB, 9C6B20, A9220F, D0CA40, D5C4B3, E8A077, FCE76E, FCFAE2, {8, 0, 0}, {32, 26, 11}, {35, 67, 9}, {43, 52, 124}, {43, 116, 9}, {67, 40, 23}, {73, 41, 16}, {93, 79, 30}, {106, 148, 171}, {156, 107, 32}, {169, 34, 15}, {208, 202, 64}, {213, 196, 179}, {232, 160, 119}, {252, 231, 110}, {252, 250, 226},
.gpl も .png も読み込めているように見える。
動作確認に使った .gpl、.png は以下。
_def16.gpl
◎ 余談。 :
.gpl内のRGB値をどうやって取り込めばいいのか悩んだけれど、sscanf() であっさり取り込めてしまった。ただ、.gpl内の記述の仕方によっては、sscanf() で対応できなくてエラーになる場面もありそう。
そもそも .gplの記述ルールがアバウト過ぎる気もする。空白無し、区切り文字は「,」にでもしておけばよかったのに。あるいは xml にしておくとか。
そもそも .gplの記述ルールがアバウト過ぎる気もする。空白無し、区切り文字は「,」にでもしておけばよかったのに。あるいは xml にしておくとか。
[ ツッコむ ]
以上、1 日分です。