2022/07/19(火) [n年前の日記]
#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 にしておくとか。
[ ツッコむ ]
以上です。