mieki256's diary



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
#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++

ソース。 :

ソースは以下。

_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

def16.png
_def16.png

余談。 :

.gpl内のRGB値をどうやって取り込めばいいのか悩んだけれど、sscanf() であっさり取り込めてしまった。ただ、.gpl内の記述の仕方によっては、sscanf() で対応できなくてエラーになる場面もありそう。

そもそも .gplの記述ルールがアバウト過ぎる気もする。空白無し、区切り文字は「,」にでもしておけばよかったのに。あるいは xml にしておくとか。

以上、1 日分です。

過去ログ表示

Prev - 2022/07 - Next
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31

カテゴリで表示

検索機能は Namazu for hns で提供されています。(詳細指定/ヘルプ


注意: 現在使用の日記自動生成システムは Version 2.19.6 です。
公開されている日記自動生成システムは Version 2.19.5 です。

Powered by hns-2.19.6, HyperNikkiSystem Project