Skip to content

Instantly share code, notes, and snippets.

@lo48576
Last active August 29, 2015 14:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lo48576/d84f467265f07bcd76ea to your computer and use it in GitHub Desktop.
Save lo48576/d84f467265f07bcd76ea to your computer and use it in GitHub Desktop.
コメント参照。
/*!
* \file main.c
* \brief
* \date 2015/02/22
* C version: C99
*/
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
// stdint.h: uint8_tとか入ってる
#include <stdint.h>
// stdlib.h: malloc()とfree()
#include <stdlib.h>
// string.h: memset()
#include <string.h>
// ファイルを開けない。
#define LOADBMP_CANNOT_OPEN_FILE 1
// BMPファイルでない。
#define LOADBMP_NON_BMP_FILE 2
// 予期せぬファイル終了。
#define LOADBMP_UNEXPECTED_EOF 3
// 破損したか不正なデータ。
#define LOADBMP_INVALID_DATA 4
// データが不正であるか、未対応のフォーマット。
#define LOADBMP_UNSUPPORTED_OR_INVALID 5
// メモリ確保に失敗。
#define LOADBMP_NOT_ENOUGH_MEMORY 6
#ifdef __cplusplus
} // extern "C"
#endif
// チョロいBMPファイルを読み込み、128x48の範囲にトリミングして、
// 白黒にしたうえで1ピクセルあたり1ビットで配列に格納。
// bufは128*48/8バイト、すなわち768バイトの領域を指している必要がある。
int load_bmp128x48(unsigned char *buf, const char *filename)
{
/*
* BMPでのデータはリトルエンディアン。
* このコードもリトルエンディアンのマシン上で
* 動かすことを想定しているので留意せよ。
*/
int ret = 0;
FILE *fp;
char c;
uint32_t info_header_size = 0;
uint32_t compression_type = 0; // 0は無圧縮。
uint32_t color_index_number_raw = 0;
uint32_t color_index_number= 0;
size_t color_pallet_elem_size = 0;
uint32_t width = 0;
int32_t height = 0; // 高さは負になることもある。
size_t bits_per_pixel = 0;
uint32_t color_masks[3] = {0}; // RGB。
/*uint8_t color_pallet[3] = {0}; // BGR。*/
uint8_t *color_pallet = NULL;
size_t line_byte_size = 0;
uint8_t *line_data = NULL;
if(!(fp = fopen(filename, "rb"))) {
// cannot open file.
return LOADBMP_CANNOT_OPEN_FILE;
}
// check magic number ("BM").
if((c = fgetc(fp)) != 'B') {
ret = LOADBMP_NON_BMP_FILE;
goto error_fclose;
} else if((c = fgetc(fp)) != 'M') {
ret = LOADBMP_NON_BMP_FILE;
goto error_fclose;
}
// 次の4バイトにはファイルサイズが格納されているはずだが、
// 信用するべきでない値なので無視する。
fseek(fp, 4L, SEEK_CUR);
// 予約された領域が2バイト*2つある。これも無視する。
fseek(fp, 2L*2, SEEK_CUR);
// 画像本体のデータの、ファイル先頭からのオフセット。
// 非0の場合はそのまま使うが、0の場合は自分で読み進めて発見する必要あり。
// どうせ使わないので無視する。
fseek(fp, 4L, SEEK_CUR);
// これでひとまずファイルヘッダは読み終えた。
// 次は情報ヘッダ。
// 情報ヘッダは複数タイプがあり、最初の4バイト(サイズが入っている)で
// 判別する。
if(fread(&info_header_size, 4, 1, fp) != 1) {
// ファイルが唐突に終わった
ret = LOADBMP_UNEXPECTED_EOF;
goto error_fclose;
}
fprintf(stderr, "header size: %d\n", info_header_size);
if(info_header_size == 12) {
// COREタイプ
if(fread(&width, 2, 1, fp) != 1) {
// ファイルが唐突に終わった
ret = LOADBMP_UNEXPECTED_EOF;
goto error_fclose;
}
if(fread(&height, 2, 1, fp) != 1) {
// ファイルが唐突に終わった
ret = LOADBMP_UNEXPECTED_EOF;
goto error_fclose;
}
// 続く2バイトはプレーン数(常に1)なので無視。
fseek(fp, 2L, SEEK_CUR);
if(fread(&bits_per_pixel, 2, 1, fp) != 1) {
// ファイルが唐突に終わった
ret = LOADBMP_UNEXPECTED_EOF;
goto error_fclose;
}
} else {
// V5タイプとして読む(これによってV4タイプとINFOタイプに同時に対応できる)。
if(fread(&width, 4, 1, fp) != 1) {
// ファイルが唐突に終わった
ret = LOADBMP_UNEXPECTED_EOF;
goto error_fclose;
}
if(fread(&height, 4, 1, fp) != 1) {
// ファイルが唐突に終わった
ret = LOADBMP_UNEXPECTED_EOF;
goto error_fclose;
}
// 続く2バイトはプレーン数(常に1)なので無視。
fseek(fp, 2L, SEEK_CUR);
if(fread(&bits_per_pixel, 2, 1, fp) != 1) {
// ファイルが唐突に終わった
ret = LOADBMP_UNEXPECTED_EOF;
goto error_fclose;
}
if(fread(&compression_type, 4, 1, fp) != 1) {
// ファイルが唐突に終わった
ret = LOADBMP_UNEXPECTED_EOF;
goto error_fclose;
}
// 続くイメージデータサイズ(4バイト)は利用しないし信用できないので無視。
// 水平解像度と垂直解像度(それぞれ4バイト)も利用しないので無視。
fseek(fp, 4L*3, SEEK_CUR);
if(fread(&color_index_number_raw, 4, 1, fp) != 1) {
// ファイルが唐突に終わった
ret = LOADBMP_UNEXPECTED_EOF;
goto error_fclose;
}
// 重要インデックス数(4バイト)は利用しないので無視する。
fseek(fp, 4L, SEEK_CUR);
// ここまでで情報ヘッダが(サイズ含め)40バイト。
if(info_header_size > 40) {
// 圧縮形式が3(BI_BITFIELDS)の場合、カラーマスクだけは必要。
if(fread(color_masks, 4, 3, fp) != 3) {
// ファイルが唐突に終わった
ret = LOADBMP_UNEXPECTED_EOF;
goto error_fclose;
}
// これ以降の情報ヘッダの情報は不要なので読み飛ばす。
fseek(fp, info_header_size-40L-12L, SEEK_CUR);
}
}
// 情報ヘッダの読み込み完了。
// 不正な値でないか確認する。
if(!(
(width == 128)
// 上から来るか下から来るかによって符号が決まる。
&& ((height == 48) || (height == -48))
// bits_per_pixel が0の場合、PNGかJPEGデータ。読めん。
&& (bits_per_pixel != 0)
// color_index_number_raw は0の場合もある。
)) {
// 不正な値が読み込まれたか、未対応のデータを読んだ。
ret = LOADBMP_INVALID_DATA;
goto error_fclose;
}
// ビットフィールドが来るかもしれない。
// 具体的には、COREヘッダ以外であって、かつ情報ヘッダでカラーマスク情報が
// 示されなかった場合にビットフィールドが出現する。
if((info_header_size == 40) && (bits_per_pixel==16 || bits_per_pixel==32) && (compression_type == 3)) {
if(fread(&color_masks, 4, 3, fp) != 3) {
// ファイルが唐突に終わった
ret = LOADBMP_UNEXPECTED_EOF;
goto error_fclose;
}
}
// カラーパレットが来るかもしれない。
// 具体的には、ピクセル毎のビット数(bits_per_pixel)が1,4,8のいずれかであるか、
// ヘッダ中で提示されたカラーインデックス数(color_index_number_raw)が1以上である場合に
// カラーパレットが出現する。
if(((bits_per_pixel&(1|4|8)) && !(bits_per_pixel&bits_per_pixel-1)) || color_index_number_raw>0) {
color_pallet_elem_size = (info_header_size == 12 ? 3 : 4);
color_index_number = (color_index_number_raw ? color_index_number_raw : (1 << bits_per_pixel));
if(!(color_pallet = (uint8_t *)malloc(sizeof(uint8_t) * color_pallet_elem_size * color_index_number))) {
ret = LOADBMP_NOT_ENOUGH_MEMORY;
goto error_fclose;
}
fprintf(stderr, "color index number: %d, color pallet elem size: %ld\n", color_index_number, color_pallet_elem_size);
if(fread(color_pallet, color_pallet_elem_size, color_index_number, fp) != color_index_number) {
// ファイルが唐突に終わった
ret = LOADBMP_UNEXPECTED_EOF;
goto error_release_color_pallet;
}
fprintf(stderr,
"pallete:\n"
"---+---+---+---+---+\n"
"idx| B | G | R |rsv|\n"
"---+---+---+---+---+\n"
);
for(int i=0; i<color_index_number; ++i) {
fprintf(stderr, "%03d| %02x| %02x| %02x| %02x|",
i,
color_pallet[color_pallet_elem_size*i],
color_pallet[color_pallet_elem_size*i + 1],
color_pallet[color_pallet_elem_size*i + 2],
(color_pallet_elem_size==4 ? color_pallet[color_pallet_elem_size*i + 3] : 0)
);
fprintf(stderr, "\n---+---+---+---+---+\n");
}
}
/*
* ここからイメージデータが始まる
*/
fprintf(stderr, "compression type: %d\nbits per pixel: %ld\n", compression_type, bits_per_pixel);
fprintf(stderr, "width: %d\nheight: %d\n", width, height);
line_byte_size = ((width*bits_per_pixel+31) / 32) * 4;
if(!(line_data = (uint8_t *)malloc(sizeof(uint8_t) * line_byte_size))) {
ret = LOADBMP_NOT_ENOUGH_MEMORY;
goto error_release_color_pallet;
}
// bufをゼロクリア
memset(buf, 0, 128/8*48);
fprintf(stderr,
"input image:\n"
" 0 "
"10 "
"20 "
"30 "
"40 "
"50 "
"60 "
"70 "
"80 "
"90 "
"100 "
"110 "
"120 127\n");
switch(compression_type) {
case 0: // 標準の非圧縮
if(bits_per_pixel == 0) {
ret = LOADBMP_INVALID_DATA;
goto error_release_line_data;
} else {
switch(bits_per_pixel) {
case 1:
{
uint8_t *current_col = NULL;
uint8_t *current_buf = NULL;
uint8_t mask = 0;
for(int row=0; row<48; ++row) {
if(fread(line_data, 1, line_byte_size, fp) != line_byte_size) {
ret = LOADBMP_UNEXPECTED_EOF;
goto error_release_line_data;
}
current_col = (uint8_t *)line_data;
mask = 0x80;
// height>0の場合、行データは下から上の順で格納されている。
current_buf = buf + (128/8) * (height>0 ? 48-1-row : row);
for(int column=0; column<128; ++column) {
// 赤成分が0の場合のみ黒とする(赤はカラーパレットの要素の3byte目)
if(color_pallet[(*current_col&mask ? color_pallet_elem_size : 0)+2]) {
// 白。
fprintf(stderr, "■");
} else {
// 黒。
fprintf(stderr, " ");
*current_buf |= mask;
}
if(!(mask>>=1)) {
mask = 0x80;
++current_buf;
++current_col;
}
}
fprintf(stderr, "| %2d\n", row);
}
break;
}
case 4:
{
uint8_t *current_col = NULL;
uint8_t *current_buf = NULL;
uint8_t mask = 0;
for(int row=0; row<48; ++row) {
if(fread(line_data, 1, line_byte_size, fp) != line_byte_size) {
ret = LOADBMP_UNEXPECTED_EOF;
goto error_release_line_data;
}
current_col = (uint8_t *)line_data;
mask = 0x80;
// height>0の場合、行データは下から上の順で格納されている。
current_buf = buf + (128/8) * (height>0 ? 48-1-row : row);
for(int column=0; column<128; ) {
// 赤成分が0の場合のみ黒とする(赤はカラーパレットの要素の3byte目)
// まずは上4bitをチェック。
if(color_pallet[(*current_col>>4)*color_pallet_elem_size+2]) {
// 白。
fprintf(stderr, "■");
} else {
// 黒。
fprintf(stderr, " ");
*current_buf |= mask;
}
// maskが0になるのは偶数回目のはずだし、ここでは大丈夫。
mask >>= 1;
// 次に下4bitをチェック。
if(color_pallet[(*current_col&0x0F)*color_pallet_elem_size+2]) {
// 白。
fprintf(stderr, "■");
} else {
// 黒。
fprintf(stderr, " ");
*current_buf |= mask;
}
++current_col;
if(!(mask>>=1)) {
mask = 0x80;
++current_buf;
}
column += 2;
}
fprintf(stderr, "| %2d\n", row);
}
break;
}
case 8:
case 16:
ret = LOADBMP_UNSUPPORTED_OR_INVALID;
fprintf(stderr, "loadbmp: unimplemented (bits_per_pixel = %ld)\n", bits_per_pixel);
goto error_release_line_data;
case 24:
if(color_index_number_raw) {
// カラーパレットが使われている
ret = LOADBMP_UNSUPPORTED_OR_INVALID;
goto error_release_line_data;
} else {
uint8_t *current_col = NULL;
uint8_t *current_buf = NULL;
uint8_t mask = 0;
for(int row=0; row<48; ++row) {
if(fread(line_data, 1, line_byte_size, fp) != line_byte_size) {
ret = LOADBMP_UNEXPECTED_EOF;
goto error_release_line_data;
}
current_col = (uint8_t *)line_data;
mask = 0x80;
// height>0の場合、行データは下から上の順で格納されている。
current_buf = buf + (128/8) * (height>0 ? 48-1-row : row);
for(int column=0; column<128; ++column) {
// 赤成分が0の場合のみ黒とする
if(*current_col) {
// 白。
fprintf(stderr, "■");
} else {
// 黒。
fprintf(stderr, " ");
*current_buf |= mask;
}
current_col += 3;
if(!(mask>>=1)) {
mask = 0x80;
++current_buf;
}
}
fprintf(stderr, "| %2d\n", row);
}
}
break;
case 32:
ret = LOADBMP_UNSUPPORTED_OR_INVALID;
fprintf(stderr, "loadbmp: unimplemented (bits_per_pixel = %ld)\n", bits_per_pixel);
goto error_release_line_data;
default:
ret = LOADBMP_INVALID_DATA;
goto error_release_line_data;
}
}
break;
case 1: // 8ビットRLE
if(bits_per_pixel != 8) {
ret = LOADBMP_INVALID_DATA;
goto error_release_line_data;
}
ret = LOADBMP_UNSUPPORTED_OR_INVALID;
goto error_release_line_data;
break;
case 2: // 4ビットRLE
if(bits_per_pixel != 4) {
ret = LOADBMP_INVALID_DATA;
goto error_release_line_data;
}
ret = LOADBMP_UNSUPPORTED_OR_INVALID;
goto error_release_line_data;
break;
case 3: // ビットフィールドによる非圧縮
if((bits_per_pixel != 16) && (bits_per_pixel != 32)) {
ret = LOADBMP_INVALID_DATA;
goto error_release_line_data;
}
ret = LOADBMP_UNSUPPORTED_OR_INVALID;
goto error_release_line_data;
break;
default:
ret = LOADBMP_UNSUPPORTED_OR_INVALID;
goto error_release_line_data;
}
error_release_line_data:
if(line_data) {
free(line_data);
}
error_release_color_pallet:
if(color_pallet) {
free(color_pallet);
}
error_fclose:
fclose(fp);
return ret;
}
int main(int argc, char **argv)
{
// height:48px, width: 128px
uint8_t image[128/8*48];
uint8_t postprocessed_image[128/8*48];
int errcode = 0;
if(argc < 2) {
fprintf(stderr, "No arguments passed. 1 or more of BMP filename required.\n");
return 1;
}
/*errcode = load_bmp128x48((void *)image, argv);*/
errcode = load_bmp128x48(image, argv[1]);
fprintf(stderr, "return code: %d\n", errcode);
switch(errcode) {
case 0:
// 正常終了。
break;
case LOADBMP_CANNOT_OPEN_FILE:
fprintf(stderr, "cannot open file: %s\n", argv[1]);
return errcode;
break;
case LOADBMP_UNEXPECTED_EOF:
fprintf(stderr, "unexpected EOF: %s\n", argv[1]);
return errcode;
break;
case LOADBMP_NOT_ENOUGH_MEMORY:
fprintf(stderr, "not enough memory.\n");
return errcode;
break;
case LOADBMP_NON_BMP_FILE:
fprintf(stderr, "not BMP: %s\n", argv[1]);
return errcode;
break;
case LOADBMP_INVALID_DATA:
fprintf(stderr, "invalid data: %s\n", argv[1]);
return errcode;
break;
case LOADBMP_UNSUPPORTED_OR_INVALID:
fprintf(stderr, "invalid data or unsupported format: %s\n", argv[1]);
return errcode;
break;
default:
fprintf(stderr, "unknown error: %d\n", errcode);
return errcode;
}
/*
* print image
*/
fprintf(stderr,
"\nLoaded image:\n"
" 0 "
"10 "
"20 "
"30 "
"40 "
"50 "
"60 "
"70 "
"80 "
"90 "
"100 "
"110 "
"120 127\n");
for(int row=0; row<48; ++row) {
const uint8_t *current_buf = image + (128/8) * row;
for(int column=0; column<128; ) {
const char *strs[16] = {
"■■■■",
"■■■ ",
"■■ ■",
"■■  ",
"■ ■■",
"■ ■ ",
"■  ■",
"■   ",
" ■■■",
" ■■ ",
" ■ ■",
" ■  ",
"  ■■",
"  ■ ",
"   ■",
"    "
};
fprintf(stderr, "%s%s", strs[(*current_buf)>>4], strs[(*current_buf)&0x0F]);
++current_buf;
column += 8;
}
fprintf(stderr, "\n");
}
/*
* ビット順を入れ替えて完成
*/
/*
* (0,1,...はsrcにおけるビット数)
* (uint8_tのMSBが0、LSBが7とする。)
* 0 1 2 3 4 5 6 7 8 ... 1022 1023
* 1024 1025 ...
* を
* 7 15 23 ... 1023 6 14 ... 1008 1016
* 1031 1039 ...
* に変換する。
* 1024ビットごとにグループ化して、6グループ全てに同じ処理を施せば完成となる。
*/
/*
* (0,1,...はdstにおけるビット数)
* 7 15 23 ...
* 6 14 22 ... <- こうして見たときの1行がsrc_row。1行あたり1024/8(=128)ビット、すなわち1024/8/8(=16)バイト
* ...
* 0 8 16 ...
*/
memset(postprocessed_image, 0, 128/8*48);
for(int row_group=0; row_group<6; ++row_group) {
const uint8_t *group_src_begin = image + (128/8*48/6) * row_group;
uint8_t *group_dst_begin = postprocessed_image + (128/8*48/6) * row_group;
// dstについて走査する。
const uint8_t *current_src = group_src_begin;
uint8_t src_mask = 0x80;
uint8_t *current_dst = group_dst_begin;
for(int column=0; column<128; ++column) {
// MSBから設定していく
*current_dst |= ((*(current_src ) & src_mask) ? (1 << 0) : 0);
*current_dst |= ((*(current_src+16*1) & src_mask) ? (1 << 1) : 0);
*current_dst |= ((*(current_src+16*2) & src_mask) ? (1 << 2) : 0);
*current_dst |= ((*(current_src+16*3) & src_mask) ? (1 << 3) : 0);
*current_dst |= ((*(current_src+16*4) & src_mask) ? (1 << 4) : 0);
*current_dst |= ((*(current_src+16*5) & src_mask) ? (1 << 5) : 0);
*current_dst |= ((*(current_src+16*6) & src_mask) ? (1 << 6) : 0);
*current_dst |= ((*(current_src+16*7) & src_mask) ? (1 << 7) : 0);
if(!(src_mask>>=1)) {
src_mask = 0x80;
++current_src;
}
++current_dst;
}
}
/*
* print post processed image
*/
fprintf(stderr,
"\nPost-processed image:\n"
" 0 "
"10 "
"20 "
"30 "
"40 "
"50 "
"60 "
"70 "
"80 "
"90 "
"100 "
"110 "
"120 127\n"
"--------------------------------------------------------------------------------------------------------------------------------"
"--------------------------------------------------------------------------------------------------------------------------------+\n"
);
for(int row_group=0; row_group<6; ++row_group) {
const uint8_t *group_buf_begin = postprocessed_image + (1024/8) * row_group;
for(int row_local=0; row_local<8; ++row_local) {
const uint8_t *current_buf = group_buf_begin;
uint8_t mask = 1 << row_local;
for(int column=0; column<128; ++column) {
fprintf(stderr, ((*current_buf & mask) ? " " : "■"));
++current_buf;
}
fprintf(stderr, "|\n");
}
fprintf(stderr,
"--------------------------------------------------------------------------------------------------------------------------------"
"--------------------------------------------------------------------------------------------------------------------------------+\n"
);
}
fprintf(stdout, "{ ");
for(int i=0; i<128/8*48-1; ++i) {
fprintf(stdout, "0x%02x, ", postprocessed_image[i]);
}
fprintf(stdout, "0x%02x }\n", postprocessed_image[128/8*48-1]);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment