Last active
August 29, 2015 14:16
-
-
Save lo48576/d84f467265f07bcd76ea to your computer and use it in GitHub Desktop.
コメント参照。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*! | |
* \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