/***************************************************************/
/* 画像ダンプデータ → bitmap画像変換        2010.5.30         */
/* kondo@kk.iij4u.or.jp         2000.08.03                     */
/*  1,4,8,24 bits 対応に拡張  Y.Fukui  2010.5.28           */
/* sizeof(char)=1, sizeof(short)=2, sizeof(int)=sizeof(long)=4 */
/***************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>

#define BUF_SIZE (1024)
#ifndef _MAX_PATH
#define _MAX_PATH (255)
#endif

static char cmdname[] = "dump2bmp";

int optind = 1; // オプションでない最初の引数までの文字列数
int optopt;     // オプション文字
char *optarg;   // オプション引数

double max = 0.0, min = 0.0;
int maxopt = 0, minopt = 0;

void usage(void)
{
  fprintf(stderr,
          "\n"
          "ASCIIテキストデータ ---> BMP フォーマット変換プログラム\n"
          "usage : %s [option] <text_file> [bitmap_file]\n"
          "  bitmap_file の指定が無い場合"
          " text_file.bmp のファイルが作成されます\n"
          "\n"
          "データ形式 : r値 g値 b値\n"
          "\tx y z\n"
          "\tx y z\n"
          "\t: : :\n"
          "\t      (改行のみでラインの区切り)\n"
          "\tx y z\n"
          "\t: : :\n"
          "\n", cmdname);
  exit(1);
}

void openerror(char *filename)
{
  fprintf(stderr, "Can't open file: %s\n", filename);
  exit(1);
}

void allocerror(char *s)
{
  fprintf(stderr, "Memory allocation error at %s.\n");
  exit(1);
}

/*----------------*/
/* ファイル名取得 */
/*----------------*/
int getbasename(char *dest, char *src)
{
  int i, start, end, ret;

  i = -1;  start = 0;  end = 0;

  // ファイル名のはじめと終わりを検出
  while (src[++i]) {
    if (src[i] == '\\' || src[i] == ':') {
      start = i + 1;    end = 0;
    }
    if (src[i] == '.')  end = i;
  }
  if (end == 0)  end = i;

  // ファイル名が有る場合
  if (start < end) {
    for (i = 0; i < end; i++) {
      dest[i] = src[i];
    }
    dest[i] = '\0';    ret = 1;
  }
  else {
    dest[0] = '\0';    ret = 0;
  }
  return ret;
}

/*----------------------*/
/* コメント行読み飛ばし */
/*----------------------*/
void commentskip(FILE * fp)
{
  int c;

  c = fgetc(fp);
// # ; / があればその行を読み飛ばし
  if ((c == '#') || (c == ';') || (c == '/')) {
    while ((c = fgetc(fp)) != '\n')   ;
  }
  ungetc(c, fp);
}

/*------------------*/
/* データ数調べ   */
/*------------------*/
void maxdata( FILE * fp, int *sx, int *sy )
// sx: 横方向の画素数、sy: 縦方向の画素数(出力)
{
  int  k, m, n, r, g, b, pixels = 0;
  char s[2], buf[BUF_SIZE];

  rewind(fp);
  commentskip(fp);
  m = 0; n = 0;  // m: 縦方向、n:横方向
  while (fgets(buf, BUF_SIZE, fp) != NULL) {
    k = sscanf(buf, "%d %d %d %1s", &r, &g, &b, s);
    if( k == 3 ) n++;  // R,G,Bのデータ行
    else {             // 空白行のとき
      if( m == 0 ){    // 最初の横方向の画素数を格納
        pixels = n;
        m++; n = 0;
      }
      else {           // 2行目以降は横方向画素数を確認
        if( n != pixels ){
          fprintf(stderr,"%d行目の画素数%dが最初%dと異なる\n"
                                             ,m+1,n,pixels);
          exit( 0 );
        }
        m++; n = 0;
      }
    }
  }
  *sx = pixels; *sy = m;
}

/*-----------------------------------------*/
/* RGBテキストデータ読み込みとパレット作成 */
/*-----------------------------------------*/
void dataread(FILE * fp, int sx, int sy,
   unsigned char **red, unsigned char **green, unsigned char **blue,
   unsigned char **index, unsigned char *pred, unsigned char *pgreen,
   unsigned char *pblue, short *bits, int *usedcolors)
{
  int i, j, ix, iy, k, n, count, over256, found;
  unsigned int r, g, b;
  unsigned char reserved = 0;
  char s[2], buf[BUF_SIZE];

  rewind(fp);
  commentskip(fp);

  // データの読み込み
  ix = 0; iy = 0;
  while (fgets(buf, BUF_SIZE, fp) != NULL) {
    n = sscanf(buf, "%d %d %d %1s", &r, &g, &b, s);

    if (feof(fp)) {
      break;
    }
    else if (n >= 3) { // 配列は、red[横方向, 縦方向]
      red[ix][iy] = (unsigned char)r;
      green[ix][iy] = (unsigned char)g;
      blue[ix][iy] = (unsigned char)b;
      ix++;
      if( ix == sx ){ iy++; ix = 0; }
    }
  }

  // データから色パレット生成
  over256 = 0; // パレット数が256以上のときに1にセット
  count = 0;  // パレットの色数
  for( j = 0; j < sy; j++ ) {
    for( i = 0; i< sx; i++ ) {
      found = 0;  // 画素の色が既に登録されているときに1にセット
      if( i==0 && j==0 ){ // 最初の色をパレットに登録
        pred[0] = red[i][j];
        pgreen[0] = green[i][j];
        pblue[0] = blue[i][j];
        index[i][j] = count; // パレット番号を画素に対応付け
        count++;
      }
      else {  // 最初でなければ、登録された色と一致するか調べる
        for( k = 0; k < count; k++ ) {
          if( red[i][j] == pred[k]
            && green[i][j] == pgreen[k]
            &&  blue[i][j] == pblue[k] ) {
            index[i][j] = k; // パレット番号を画素に対応付け
            found = 1; // パレットに登録済みの色であった
            break;
          }
        }
        if( found == 0 ) { // 登録されてない場合パレット追加
          pred[count] = red[i][j];
          pgreen[count] = green[i][j];
          pblue[count] = blue[i][j];
          index[i][j] = count;
          if( count < 255 ) count++;
          else {
            over256 = 1;  // 24 bits 対応なので
            i = sx; j = sy; break; // パレット作成中断
          }
        }
      }
      // 画素の色をパレットに登録されているか調べるルーチンの終わり
    }              
  }
  
  *usedcolors = count;
  if( over256 == 1 ) *bits = 24;
  else if( count > 16 ) *bits = 8;
  else if( count > 2 )  *bits = 4;
  else *bits = 1;
}

/*---------------------------------*/
/* ファイルヘッダ BITMAPFILEHEADER */
/*---------------------------------*/
int fileheader(FILE * fp,
               long size, long offset)
{
  long count, filesize;
  short reserved = 0;
  char s[2];

  rewind(fp);

  // 識別文字 BM
  s[0] = 'B';
  s[1] = 'M';
  fwrite(s, sizeof(char), 2, fp);

  printf("[BITMAPFILEHEADER]\n");
  // ファイルサイズ bfSize
  filesize = size + offset;
  printf("  File Size          = %ld[Bytes]\n", filesize);
  fwrite(&filesize, sizeof(long), 1, fp);
  // 予約エリア bfReserved1
  fwrite(&reserved, sizeof(short), 1, fp);
  // 予約エリア bfReserved2
  fwrite(&reserved, sizeof(short), 1, fp);
  // データ部までのオフセット bfOffBits
  printf("  Bitmap Data Offset = %ld [Bytes]\n", offset);
  fwrite(&offset, sizeof(long), 1, fp);

  // ファイルヘッダサイズ 14 Byte
  if ((count = ftell(fp)) != 14) {
    fprintf(stderr, "BITMAPFILEHEADER write error : %ld\n", count);
    exit(1);
  }
  return count;
}

/*-----------------------------*/
/* 情報ヘッダ BITMAPINFOHEADER */
/*-----------------------------*/
int bitmapheader(FILE * fp,
     long width, long height, long size, short bits)
{
  long count, var_long;
  short var_short;

  fseek(fp, 14, SEEK_SET);

  printf("[BITMAPINFOHEADER]\n");
  // 情報ヘッダのサイズ biSize (Windows BMP は 40)
  var_long = 40;
  fwrite(&var_long, sizeof(long), 1, fp);
  // 画像の幅 biWidth
  var_long = width;
  printf("  Width              = %ld [pixels]\n", var_long);
  fwrite(&var_long, sizeof(long), 1, fp);
  // 画像の高さ biHeight
  // (正数ならば左下から右上, マイナスならば左上から右下)
  var_long = height;
  printf("  Height             = %ld [pixels]\n", var_long);
  fwrite(&var_long, sizeof(long), 1, fp);
  // プレーン数 biPlanes (必ず 1)
  var_short = 1;
  printf("  Planes             = %hd\n", var_short);
  fwrite(&var_short, sizeof(short), 1, fp);
  // 1ピクセルのデータ数 biBitCount (1, 4, 8, 24, 32)
  var_short = bits;
  printf("  Bits Per Pixel     = %hd [bits]\n", var_short);
  fwrite(&var_short, sizeof(short), 1, fp);

  // 圧縮 biCompression (無圧縮ならば 0)
  var_long = 0;
  printf("  Compression        = %ld\n", var_long);
  fwrite(&var_long, sizeof(long), 1, fp);
  // 画像のサイズ biSizeImage
  var_long = size;
  printf("  Bitmap Data Size   = %ld [Bytes]\n", var_long);
  fwrite(&var_long, sizeof(long), 1, fp);
  // 横方向解像度 pixel/m biXPelPerMeter
  // (96dpi, 1inch = 0.0254m のとき 96/0.0254 = 3780)
  var_long = 3780;
  printf("  HResolution        = %ld [pixel/m]\n", var_long);
  fwrite(&var_long, sizeof(long), 1, fp);
  // 縦方向解像度 pixel/m biYPelPerMeter
  // (96dpi, 1inch = 0.0254m のとき 96/0.0254 = 3780)
  var_long = 3780;
  printf("  VResolution        = %ld [pixel/m]\n", var_long);
  fwrite(&var_long, sizeof(long), 1, fp);
  // パレット数 biClrUsed
  var_long = 1 << bits;
  printf("  Colors             = %ld [colors]\n", var_long);
  fwrite(&var_long, sizeof(long), 1, fp);
  // パレット中の重要な色  biClrImportant
  var_long = 0;
  printf("  Important Colors   = %ld\n", var_long);
  fwrite(&var_long, sizeof(long), 1, fp);

  // ファイルヘッダ(14 Byte) + 情報ヘッダサイズ(40 Byte)
  if ((count = ftell(fp)) != 54) {
    fprintf(stderr, "BITMAPINFOHEADER write error : %ld\n", count);
    exit(1);
  }
  return count;
}

/*------------------------*/
/* パレットデータ RGBQUAD */
/*------------------------*/

long rgbquad(FILE * fp, short bits, unsigned char *red,
              unsigned char *green, unsigned char *blue, int usedcolors)
{
  int  i;
  long color, count;
  unsigned char reserved = 0;

  color = 1 << bits;
  printf("  Pallete used       = %ld [colors] (%ld [Bytes])\n",
         usedcolors, color);
    
  fseek(fp, 54, SEEK_SET);

  // パレットを作成(書き出す)
  for (i = 0; i < usedcolors; i++) {
    fputc(blue[i], fp);
    fputc(green[i], fp);
    fputc(red[i], fp);
    fputc(reserved, fp);
  }
  for( i=usedcolors; i< color; i++ ){
    fputc(reserved, fp);
    fputc(reserved, fp);
    fputc(reserved, fp);
    fputc(reserved, fp);
  }

  if ((count = ftell(fp)) != 54 + 4 * color) {
    fprintf(stderr, "RGBQUAD write error : %ld\n", count);
    exit(1);
  }
  return count;
}

/*--------------------*/
/* 画像データ書き出し */
/*--------------------*/
long imagewrite(FILE * fp, int x, int y, short bits,
   unsigned char **red, unsigned char **green, unsigned char **blue,
   unsigned char **index, long offset)
{
  long i, j;
  unsigned char pack, zero = 0;
  int sizechar, x1, padding;

  fseek(fp, offset, SEEK_SET);
  sizechar = sizeof(char);

  // 24ビットデータの場合
  if( bits == 24 ){
    // BGRの3色(3Bytes)をx個の画素を出力したときのBytes数が4の倍数でない時
    if( (i = (3*x) % 4) != 0 ) {  // 4の倍数にするためのByte数はx1
      padding = 1;   x1 = 4 - i;
    }
    else { padding = 0; }
    for (j = y-1; j >= 0; j--) {
      for (i = 0; i < x; i++) {
        fwrite(&blue[i][j], sizechar, 1, fp);
        fwrite(&green[i][j], sizechar, 1, fp);
        fwrite(&red[i][j], sizechar, 1, fp);
      }
      // 1ラインは 4Byte(long) 境界にあわせるための詰め物
      if ( padding == 1 ) {
        for (i = 0; i < x1; i++) {
          fwrite(&zero, sizechar, 1, fp);
        }
      }
    }
    return ftell(fp);
  }
  // 8ビットの場合 1パレット番号を1バイトに書く
  else if( bits == 8 ){
    if( (i = x % 4) != 0 ){
      padding = 1;   x1 = 4 - i;
    } else { padding = 0; }
    for (j = y-1; j >= 0; j--) {
      for (i = 0; i < x; i++) {  
        fwrite(&index[i][j], sizechar, 1, fp);
      }
      if ( padding == 1 ) { // 4バイト単位で1行を描くための詰め物
        for (i = 0; i < x1; i++) {
          fwrite(&zero, sizechar, 1, fp);
        }
      }
    }
    return ftell(fp);
  }

  // 4ビットの場合 2つのパレット番号を1バイトにまとめて書く
  else if( bits == 4 ){
    x1 = ((x - 1)/8 + 1)*8; // 32ビット単位で書き出すため
    for (j = y-1; j >= 0; j--) {
      for (i = 0; i < x; i++) {  
        if((i % 2) == 0 ) pack = (0xf & index[i][j]) << 4;
        else {
          pack |= (0xf & index[i][j]);
          fwrite(&pack, sizechar, 1, fp);
          pack = 0;
        }
      }
      for (i = x; i < x1; i++) {
        if((i % 2) == 1) {
          pack |= (0xf & zero);
          fwrite(&pack, sizechar, 1, fp);
          pack = 0;
        }
      }
    }
    return ftell(fp);
  }
  // 1ビットの場合 8画素ごとに1バイトにまとめて書く
  else if( bits == 1 ){
    x1 = ((x - 1)/32 + 1)*32; // 32ビット単位で書き出すため
    for (j = y-1; j >= 0; j--) {
      pack = 0;
      for (i = 0; i < x; i++) {  
        if(((i+1) % 8) != 0 ) {
          pack |= (1 & index[i][j]) << (7-(i%8));
        }
        else {
          pack |= (1 & index[i][j]);
          fwrite(&pack, sizechar, 1, fp);
          pack = 0;
        }
      }
      for (i = x; i < x1; i++) {
        if(((i+1) % 8) == 0) {
          fwrite(&pack, sizechar, 1, fp);
          pack = 0;
        }
      }
    }
    return ftell(fp);
  }
  // その他の場合
  else {
    printf("%dビット画像は対応しません。\n",bits);
    exit(0);
  }
}

/*-----------------*/
/* BITMAP 書き込み */
/*-----------------*/
void bmpwrite(char *txtfile, char *bmpfile)
{
  int i, j, sx, sy, sx1, usedcolors;
  short bits;
  long size, offset;
  double maxx, minx, maxy, miny;
  unsigned char **red, **green, **blue;
  // パレット情報保存用配列
  unsigned char pred[256], pgreen[256], pblue[256], **index;
  FILE *fp1, *fp2;

// R, G, B テキストデータ読み込みファイルオープン

  if ((fp1 = fopen(txtfile, "r")) == NULL) {
    openerror(txtfile);
    exit(1);
  }

  // データ数調べ
  maxdata(fp1, &sx, &sy);

  // メモリ確保 R, G, B + パレット用インデックス
  if ((red = malloc(sx * sizeof(char *))) == NULL) {
    allocerror("redi");
  }
  for (i = 0; i < sx; i++) {
    if ((red[i] = malloc(sy * sizeof(char))) == NULL) {
      allocerror("redj");
    }
    for (j = 0; j < sy; j++) {
      red[i][j] = 0;
    }
  }

  if ((green = malloc(sx * sizeof(char *))) == NULL) {
    allocerror("greeni");
  }
  for (i = 0; i < sx; i++) {
    if ((green[i] = malloc(sy * sizeof(char))) == NULL) {
      allocerror("greenj");
    }
    for (j = 0; j < sy; j++) {
      green[i][j] = 0;
    }
  }

  if ((blue = malloc(sx * sizeof(char *))) == NULL) {
    allocerror("bluei");
  }
  for (i = 0; i < sx; i++) {
    if ((blue[i] = malloc(sy * sizeof(char))) == NULL) {
      allocerror("bluej");
    }
    for (j = 0; j < sy; j++) {
      blue[i][j] = 0;
    }
  }

  if ((index = malloc(sx * sizeof(char *))) == NULL) {
    allocerror("indexi");
  }
  for (i = 0; i < sx; i++) {
    if ((index[i] = malloc(sy * sizeof(char))) == NULL) {
      allocerror("indexj");
    }
    for (j = 0; j < sy; j++) {
      index[i][j] = 0;
    }
  }

  // データ読み込み
  dataread(fp1, sx, sy, // 入力引数
    red, green, blue, index, pred, pgreen, pblue, &bits, &usedcolors); // 出力引数

  fclose(fp1);

//  画像サイズ (1ラインは4Byte(long)境界にあわせる)
  if( bits == 1 || bits == 4 || bits == 8 ){
    if (sx % 4 != 0) { sx1 = (sx / 4 + 1)*4;  }
    else {   sx1 = sx;    }
    size = sx1 * sy;
  }
  else if( bits == 24 || bits == 32 ){
    size = sx * sy;
  }
  else {
    printf("%dbits画像データはサポート外\n");
    exit( 0 );
  }

// パレットを使わない場合(24ビットの場合)
  if (sx % 4 != 0) {
    sx1 = (sx / 4 + 1)*4;
  }
  else sx1 = sx;
  size = sx1 * sy * 3;

// 画像データまでのオフセット(ヘッダサイズ)
  // パレットを使う場合( bits = 1, 4, 8 )
  if( bits ==1 || bits == 4 || bits == 8 )
    offset = 14 + 40 + 4 * (1 << bits);
  // 24ビットのときはパレット無し
  else if( bits == 24 || bits == 32 ) offset = 14 + 40;

// 出力ファイルオープン
  if ((fp2 = fopen(bmpfile, "wb")) == NULL) {
    openerror(bmpfile);
  }
  // ファイルヘッダ
  fileheader(fp2, size, offset);
  // 情報ヘッダ
  bitmapheader(fp2, (long) sx, (long) sy, size, bits);
  // パレットデータ(パレット有りの場合)
  if( bits==1 || bits==4 || bits==8 ) {
    rgbquad(fp2, bits, pred, pgreen, pblue, usedcolors);
  }
  // 画像データ
  imagewrite(fp2, sx, sy, bits, red, green, blue, index, offset);
  free(red);  free(green);  free(blue); free(index);
  fclose(fp2);
}

/**************/
/* メイン関数 */
/**************/
int main(int argc, char *argv[])
{
  int c;
  char txtfile[_MAX_PATH], bmpfile[_MAX_PATH], basename[_MAX_PATH];


  if (argc - optind < 1) {  usage();  }
  else if (argc - optind == 1) {
    strcpy(txtfile, argv[optind]);
    getbasename(basename, argv[optind]);
    sprintf(bmpfile, "%s.bmp", basename);
  }
  else {
    strcpy(txtfile, argv[optind]);
    strcpy(bmpfile, argv[optind + 1]);
  }

  bmpwrite(txtfile, bmpfile);
  return 0;
}
最終更新:2010年06月01日 20:48